diff --git a/.github/workflows/build_wasm.yml b/.github/workflows/build_wasm.yml index 1ac58ae20..6f6b7df32 100644 --- a/.github/workflows/build_wasm.yml +++ b/.github/workflows/build_wasm.yml @@ -30,8 +30,7 @@ env: libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev libfreetype6-dev libx11-dev libxcomposite-dev libxcursor-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libglu1-mesa-dev mesa-common-dev - EM_VERSION: 3.1.45 - EM_CACHE_FOLDER: "emsdk-cache" + EM_VERSION: 4.0.21 jobs: build_tests: @@ -46,7 +45,6 @@ jobs: uses: mymindstorm/setup-emsdk@v14 with: version: ${{ env.EM_VERSION }} - actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} - name: Configure run: emcmake cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON -DYUP_ENABLE_EXAMPLES=ON - run: cmake --build ${{ runner.workspace }}/build --config Debug --parallel 4 --target yup_tests @@ -68,7 +66,6 @@ jobs: uses: mymindstorm/setup-emsdk@v14 with: version: ${{ env.EM_VERSION }} - actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} - name: Configure run: emcmake cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON -DYUP_ENABLE_EXAMPLES=ON - run: cmake --build ${{ runner.workspace }}/build --config Debug --parallel 4 --target example_console @@ -86,7 +83,6 @@ jobs: uses: mymindstorm/setup-emsdk@v14 with: version: ${{ env.EM_VERSION }} - actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} - name: Configure run: emcmake cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON -DYUP_ENABLE_EXAMPLES=ON - run: cmake --build ${{ runner.workspace }}/build --config Debug --parallel 4 --target example_app @@ -104,7 +100,6 @@ jobs: uses: mymindstorm/setup-emsdk@v14 with: version: ${{ env.EM_VERSION }} - actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} - name: Configure run: emcmake cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON -DYUP_ENABLE_EXAMPLES=ON - run: cmake --build ${{ runner.workspace }}/build --config Debug --parallel 4 --target example_graphics diff --git a/CMakeLists.txt b/CMakeLists.txt index 217f1113a..8f3bc6f00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,12 +17,14 @@ # # ============================================================================== -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) include (cmake/yup.cmake) _yup_get_project_version_string (${CMAKE_CURRENT_LIST_DIR}/modules yup_version) _yup_message (STATUS "Building project version ${yup_version}") +set (CMAKE_CXX_SCAN_FOR_MODULES 0) + if (YUP_PLATFORM_MAC) set (CMAKE_OSX_DEPLOYMENT_TARGET 11.0) set (CMAKE_XCODE_GENERATE_SCHEME OFF) diff --git a/README.md b/README.md index 33bfc4b9f..90480de6a 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ YUP brings a suite of powerful features, including: ## Prerequisites Before building, ensure you have a: -- C++17-compliant compiler +- C++20-compliant compiler - CMake 3.28 or later @@ -293,7 +293,7 @@ START_YUP_APPLICATION (MyApplication) And add this as `CMakeLists.txt`: ```cmake -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) set (target_name my_app) set (target_version "0.0.1") diff --git a/cmake/toolchains/ios.cmake b/cmake/toolchains/ios.cmake index 514328359..4d391948b 100644 --- a/cmake/toolchains/ios.cmake +++ b/cmake/toolchains/ios.cmake @@ -154,7 +154,7 @@ # command. # -cmake_minimum_required(VERSION 3.16.0) +cmake_minimum_required(VERSION 3.31) # CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds. if(DEFINED ENV{_IOS_TOOLCHAIN_HAS_RUN}) diff --git a/cmake/yup_audio_plugin.cmake b/cmake/yup_audio_plugin.cmake index 4d80314c0..c9600dc19 100644 --- a/cmake/yup_audio_plugin.cmake +++ b/cmake/yup_audio_plugin.cmake @@ -36,7 +36,7 @@ function (yup_audio_plugin) cmake_parse_arguments (YUP_ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) - _yup_set_default (YUP_ARG_TARGET_CXX_STANDARD 17) + _yup_set_default (YUP_ARG_TARGET_CXX_STANDARD 20) set (target_name "${YUP_ARG_TARGET_NAME}") set (target_version "${YUP_ARG_TARGET_VERSION}") diff --git a/cmake/yup_dependencies.cmake b/cmake/yup_dependencies.cmake index df7b29848..2fa317996 100644 --- a/cmake/yup_dependencies.cmake +++ b/cmake/yup_dependencies.cmake @@ -89,7 +89,7 @@ function (_yup_fetch_perfetto) FetchContent_MakeAvailable (Perfetto) add_library (perfetto STATIC) - target_compile_features (perfetto PUBLIC cxx_std_17) + target_compile_features (perfetto PUBLIC cxx_std_20) target_sources (perfetto PRIVATE "$" diff --git a/cmake/yup_modules.cmake b/cmake/yup_modules.cmake index d313a8be2..a73f8cfe6 100644 --- a/cmake/yup_modules.cmake +++ b/cmake/yup_modules.cmake @@ -183,7 +183,7 @@ function (_yup_module_setup_target module_name if (module_cpp_standard) target_compile_features (${module_name} INTERFACE cxx_std_${module_cpp_standard}) else() - target_compile_features (${module_name} INTERFACE cxx_std_17) + target_compile_features (${module_name} INTERFACE cxx_std_20) endif() set_target_properties (${module_name} PROPERTIES @@ -366,7 +366,7 @@ function (yup_add_module module_path modules_definitions module_group) endif() endforeach() - _yup_set_default (module_cpp_standard "17") + _yup_set_default (module_cpp_standard "20") _yup_set_default (module_arc_enabled OFF) _yup_set_default (module_needs_python OFF) _yup_resolve_variable_paths ("${module_searchpaths}" module_searchpaths) diff --git a/cmake/yup_platforms.cmake b/cmake/yup_platforms.cmake index f8db200b1..47d8ffdcc 100644 --- a/cmake/yup_platforms.cmake +++ b/cmake/yup_platforms.cmake @@ -37,7 +37,7 @@ function (_yup_prepare_gradle_android) _yup_set_default (YUP_ANDROID_TOOLCHAIN "clang") _yup_set_default (YUP_ANDROID_PLATFORM "android-${YUP_ANDROID_MIN_SDK_VERSION}") _yup_set_default (YUP_ANDROID_STL "c++_shared") - _yup_set_default (YUP_ANDROID_CPP_VERSION "17") + _yup_set_default (YUP_ANDROID_CPP_VERSION "20") _yup_set_default (YUP_ANDROID_APPLICATION_NAMESPACE "org.yup") _yup_set_default (YUP_ANDROID_APPLICATION_ID "org.yup.default_app") _yup_set_default (YUP_ANDROID_APPLICATION_VERSION "1.0") diff --git a/cmake/yup_python.cmake b/cmake/yup_python.cmake index 96822022e..a39133f9e 100644 --- a/cmake/yup_python.cmake +++ b/cmake/yup_python.cmake @@ -26,7 +26,7 @@ function (yup_prepare_python_stdlib target_name python_tools_path output_variabl cmake_parse_arguments (YUP_ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) - set (default_ignored_library_patterns "lib2to3" "pydoc_data" "_xxtestfuzz*") + set (default_ignored_library_patterns "lib2to3") set (ignored_library_patterns ${default_ignored_library_patterns}) list (APPEND ignored_library_patterns ${YUP_ARG_IGNORED_LIBRARY_PATTERNS}) @@ -45,7 +45,7 @@ function (yup_prepare_python_stdlib target_name python_tools_path output_variabl get_filename_component (python_root_path "${python_embed_env_SOURCE_DIR}" REALPATH) else() - get_filename_component (python_root_path "${Python_LIBRARY_DIRS}" REALPATH) + get_filename_component (python_root_path "${Python_LIBRARY_DIRS}/.." REALPATH) endif() _yup_message (STATUS "Executing python stdlib archive generator tool") diff --git a/cmake/yup_standalone.cmake b/cmake/yup_standalone.cmake index e4c4aa632..0b728a5a6 100644 --- a/cmake/yup_standalone.cmake +++ b/cmake/yup_standalone.cmake @@ -35,7 +35,7 @@ function (yup_standalone_app) cmake_parse_arguments (YUP_ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) - _yup_set_default (YUP_ARG_TARGET_CXX_STANDARD 17) + _yup_set_default (YUP_ARG_TARGET_CXX_STANDARD 20) _yup_set_default (YUP_ARG_TARGET_ICON "${CMAKE_SOURCE_DIR}/cmake/resources/app-icon.png") set (target_name "${YUP_ARG_TARGET_NAME}") @@ -177,7 +177,6 @@ function (yup_standalone_app) -sASSERTIONS=1 -sDISABLE_EXCEPTION_CATCHING=0 -sERROR_ON_UNDEFINED_SYMBOLS=1 - -sDEMANGLE_SUPPORT=1 -sSTACK_OVERFLOW_CHECK=2 -sFORCE_FILESYSTEM=1 -sNODERAWFS=0 @@ -185,7 +184,7 @@ function (yup_standalone_app) -sFETCH=1 #-sASYNCIFY=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap - -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$dynCall' + -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$dynCall','$stackTrace' --shell-file "${YUP_ARG_CUSTOM_SHELL}") foreach (preload_file ${YUP_ARG_PRELOAD_FILES}) diff --git a/docs/Building Plugins.md b/docs/Building Plugins.md index b1fd5184f..61504ab1e 100644 --- a/docs/Building Plugins.md +++ b/docs/Building Plugins.md @@ -100,7 +100,7 @@ extern "C" yup::AudioProcessor* createPluginProcessor() Create a `CMakeLists.txt` file for your plugin: ```cmake -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) set (target_name my_plugin) set (target_version "0.0.1") @@ -124,7 +124,7 @@ yup_audio_plugin ( TARGET_IDE_GROUP "MyPlugin" TARGET_APP_ID "com.mycompany.${target_name}" TARGET_APP_NAMESPACE "com.mycompany" - TARGET_CXX_STANDARD 17 + TARGET_CXX_STANDARD 20 PLUGIN_ID "com.mycompany.MyPlugin" PLUGIN_NAME "MyPlugin" PLUGIN_VENDOR "com.mycompany" diff --git a/docs/Building Standalone.md b/docs/Building Standalone.md index b98d440bb..51163d114 100644 --- a/docs/Building Standalone.md +++ b/docs/Building Standalone.md @@ -86,7 +86,7 @@ START_YUP_APPLICATION (MyApplication) Create a `CMakeLists.txt` file for your application: ```cmake -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) set (target_name my_app) set (target_version "1.0.0") @@ -110,7 +110,7 @@ yup_standalone_app ( TARGET_IDE_GROUP "MyApp" TARGET_APP_ID "com.mycompany.${target_name}" TARGET_APP_NAMESPACE "com.mycompany" - TARGET_CXX_STANDARD 17 + TARGET_CXX_STANDARD 20 INITIAL_MEMORY 268435456 # 256MB initial memory MODULES yup_audio_devices diff --git a/examples/app/CMakeLists.txt b/examples/app/CMakeLists.txt index b79fc8cc4..ec5cde285 100644 --- a/examples/app/CMakeLists.txt +++ b/examples/app/CMakeLists.txt @@ -17,7 +17,7 @@ # # ============================================================================== -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) # ==== Set target name and version set (target_name example_app) diff --git a/examples/console/CMakeLists.txt b/examples/console/CMakeLists.txt index 28d202b08..839f36c99 100644 --- a/examples/console/CMakeLists.txt +++ b/examples/console/CMakeLists.txt @@ -17,7 +17,7 @@ # # ============================================================================== -cmake_minimum_required(VERSION 3.28) +cmake_minimum_required(VERSION 3.31) # ==== Prepare target set (target_name example_console) diff --git a/examples/graphics/CMakeLists.txt b/examples/graphics/CMakeLists.txt index cd7a2344e..6f97a56ff 100644 --- a/examples/graphics/CMakeLists.txt +++ b/examples/graphics/CMakeLists.txt @@ -17,7 +17,7 @@ # # ============================================================================== -cmake_minimum_required (VERSION 3.28) +cmake_minimum_required (VERSION 3.31) # ==== Set target name and version set (target_name example_graphics) diff --git a/examples/plugin/CMakeLists.txt b/examples/plugin/CMakeLists.txt index 27f8da425..ba1eee55c 100644 --- a/examples/plugin/CMakeLists.txt +++ b/examples/plugin/CMakeLists.txt @@ -17,7 +17,7 @@ # # ============================================================================== -cmake_minimum_required(VERSION 3.28) +cmake_minimum_required(VERSION 3.31) # ==== Prepare target set (target_name example_plugin) @@ -31,7 +31,7 @@ yup_audio_plugin ( TARGET_IDE_GROUP "Examples" TARGET_APP_ID "org.yup.${target_name}" TARGET_APP_NAMESPACE "org.yup" - TARGET_CXX_STANDARD 17 + TARGET_CXX_STANDARD 20 PLUGIN_ID "org.yup.YupCLAP" PLUGIN_NAME "YupCLAPPZ" PLUGIN_VENDOR "org.yup" diff --git a/modules/yup_audio_basics/audio_play_head/yup_AudioPlayHead.h b/modules/yup_audio_basics/audio_play_head/yup_AudioPlayHead.h index 051b30c89..87e1d3c3a 100644 --- a/modules/yup_audio_basics/audio_play_head/yup_AudioPlayHead.h +++ b/modules/yup_audio_basics/audio_play_head/yup_AudioPlayHead.h @@ -361,43 +361,43 @@ class YUP_API AudioPlayHead { public: /** Returns the number of samples that have elapsed. */ - Optional getTimeInSamples() const { return getOptional (flagTimeSamples, timeInSamples); } + std::optional getTimeInSamples() const { return getOptional (flagTimeSamples, timeInSamples); } /** @see getTimeInSamples() */ - void setTimeInSamples (Optional timeInSamplesIn) { setOptional (flagTimeSamples, timeInSamples, timeInSamplesIn); } + void setTimeInSamples (std::optional timeInSamplesIn) { setOptional (flagTimeSamples, timeInSamples, timeInSamplesIn); } /** Returns the number of seconds that have elapsed. */ - Optional getTimeInSeconds() const { return getOptional (flagTimeSeconds, timeInSeconds); } + std::optional getTimeInSeconds() const { return getOptional (flagTimeSeconds, timeInSeconds); } /** @see getTimeInSamples() */ - void setTimeInSeconds (Optional timeInSecondsIn) { setOptional (flagTimeSeconds, timeInSeconds, timeInSecondsIn); } + void setTimeInSeconds (std::optional timeInSecondsIn) { setOptional (flagTimeSeconds, timeInSeconds, timeInSecondsIn); } /** Returns the bpm, if available. */ - Optional getBpm() const { return getOptional (flagTempo, tempoBpm); } + std::optional getBpm() const { return getOptional (flagTempo, tempoBpm); } /** @see getBpm() */ - void setBpm (Optional bpmIn) { setOptional (flagTempo, tempoBpm, bpmIn); } + void setBpm (std::optional bpmIn) { setOptional (flagTempo, tempoBpm, bpmIn); } /** Returns the time signature, if available. */ - Optional getTimeSignature() const { return getOptional (flagTimeSignature, timeSignature); } + std::optional getTimeSignature() const { return getOptional (flagTimeSignature, timeSignature); } /** @see getTimeSignature() */ - void setTimeSignature (Optional timeSignatureIn) { setOptional (flagTimeSignature, timeSignature, timeSignatureIn); } + void setTimeSignature (std::optional timeSignatureIn) { setOptional (flagTimeSignature, timeSignature, timeSignatureIn); } /** Returns host loop points, if available. */ - Optional getLoopPoints() const { return getOptional (flagLoopPoints, loopPoints); } + std::optional getLoopPoints() const { return getOptional (flagLoopPoints, loopPoints); } /** @see getLoopPoints() */ - void setLoopPoints (Optional loopPointsIn) { setOptional (flagLoopPoints, loopPoints, loopPointsIn); } + void setLoopPoints (std::optional loopPointsIn) { setOptional (flagLoopPoints, loopPoints, loopPointsIn); } /** The number of bars since the beginning of the timeline. This value isn't available in all hosts or in all plugin formats. */ - Optional getBarCount() const { return getOptional (flagBarCount, barCount); } + std::optional getBarCount() const { return getOptional (flagBarCount, barCount); } /** @see getBarCount() */ - void setBarCount (Optional barCountIn) { setOptional (flagBarCount, barCount, barCountIn); } + void setBarCount (std::optional barCountIn) { setOptional (flagBarCount, barCount, barCountIn); } /** The position of the start of the last bar, in units of quarter-notes. @@ -406,40 +406,40 @@ class YUP_API AudioPlayHead Note - this value may be unavailable on some hosts, e.g. Pro-Tools. */ - Optional getPpqPositionOfLastBarStart() const { return getOptional (flagLastBarStartPpq, lastBarStartPpq); } + std::optional getPpqPositionOfLastBarStart() const { return getOptional (flagLastBarStartPpq, lastBarStartPpq); } /** @see getPpqPositionOfLastBarStart() */ - void setPpqPositionOfLastBarStart (Optional positionIn) { setOptional (flagLastBarStartPpq, lastBarStartPpq, positionIn); } + void setPpqPositionOfLastBarStart (std::optional positionIn) { setOptional (flagLastBarStartPpq, lastBarStartPpq, positionIn); } /** The video frame rate, if available. */ - Optional getFrameRate() const { return getOptional (flagFrameRate, frame); } + std::optional getFrameRate() const { return getOptional (flagFrameRate, frame); } /** @see getFrameRate() */ - void setFrameRate (Optional frameRateIn) { setOptional (flagFrameRate, frame, frameRateIn); } + void setFrameRate (std::optional frameRateIn) { setOptional (flagFrameRate, frame, frameRateIn); } /** The current play position, in units of quarter-notes. */ - Optional getPpqPosition() const { return getOptional (flagPpqPosition, positionPpq); } + std::optional getPpqPosition() const { return getOptional (flagPpqPosition, positionPpq); } /** @see getPpqPosition() */ - void setPpqPosition (Optional ppqPositionIn) { setOptional (flagPpqPosition, positionPpq, ppqPositionIn); } + void setPpqPosition (std::optional ppqPositionIn) { setOptional (flagPpqPosition, positionPpq, ppqPositionIn); } /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ - Optional getEditOriginTime() const { return getOptional (flagOriginTime, originTime); } + std::optional getEditOriginTime() const { return getOptional (flagOriginTime, originTime); } /** @see getEditOriginTime() */ - void setEditOriginTime (Optional editOriginTimeIn) { setOptional (flagOriginTime, originTime, editOriginTimeIn); } + void setEditOriginTime (std::optional editOriginTimeIn) { setOptional (flagOriginTime, originTime, editOriginTimeIn); } /** Get the host's callback time in nanoseconds, if available. */ - Optional getHostTimeNs() const { return getOptional (flagHostTimeNs, hostTimeNs); } + std::optional getHostTimeNs() const { return getOptional (flagHostTimeNs, hostTimeNs); } /** @see getHostTimeNs() */ - void setHostTimeNs (Optional hostTimeNsIn) { setOptional (flagHostTimeNs, hostTimeNs, hostTimeNsIn); } + void setHostTimeNs (std::optional hostTimeNsIn) { setOptional (flagHostTimeNs, hostTimeNs, hostTimeNsIn); } /** The current play position, in samples from the start of processing, without looping, if available. */ - Optional getContinuousTimeInSamples() const { return getOptional (flagContinuousTime, continuousTimeInSamples); } + std::optional getContinuousTimeInSamples() const { return getOptional (flagContinuousTime, continuousTimeInSamples); } /** @see getContinuousTimeInSamples() */ - void setContinuousTimeInSamples (Optional cont) { setOptional (flagContinuousTime, continuousTimeInSamples, cont); } + void setContinuousTimeInSamples (std::optional cont) { setOptional (flagContinuousTime, continuousTimeInSamples, cont); } /** True if the transport is currently playing. */ bool getIsPlaying() const { return getFlag (flagIsPlaying); } @@ -502,18 +502,18 @@ class YUP_API AudioPlayHead } template - Optional getOptional (int64_t flagToCheck, Value value) const + std::optional getOptional (int64_t flagToCheck, Value value) const { - return getFlag (flagToCheck) ? makeOptional (std::move (value)) : nullopt; + return getFlag (flagToCheck) ? std::make_optional (std::move (value)) : std::nullopt; } template - void setOptional (int64_t flagToCheck, Value& value, Optional opt) + void setOptional (int64_t flagToCheck, Value& value, std::optional opt) { - if (opt.hasValue()) + if (opt.has_value()) value = *opt; - setFlag (flagToCheck, opt.hasValue()); + setFlag (flagToCheck, opt.has_value()); } enum @@ -564,7 +564,7 @@ class YUP_API AudioPlayHead in which a time would make sense, and some hosts will almost certainly have multithreading issues if it's not called on the audio thread. */ - virtual Optional getPosition() const = 0; + virtual std::optional getPosition() const = 0; /** Returns true if this object can control the transport. */ virtual bool canControlTransport(); diff --git a/modules/yup_audio_basics/midi/yup_MidiFile.cpp b/modules/yup_audio_basics/midi/yup_MidiFile.cpp index 4452c348b..232b547eb 100644 --- a/modules/yup_audio_basics/midi/yup_MidiFile.cpp +++ b/modules/yup_audio_basics/midi/yup_MidiFile.cpp @@ -79,7 +79,7 @@ struct ReadTrait }; template -Optional tryRead (const uint8*& data, size_t& remaining) +std::optional tryRead (const uint8*& data, size_t& remaining) { using Trait = ReadTrait; constexpr auto size = sizeof (Integral); @@ -87,7 +87,7 @@ Optional tryRead (const uint8*& data, size_t& remaining) if (remaining < size) return {}; - const Optional result { Trait::read (data) }; + const std::optional result { Trait::read (data) }; data += size; remaining -= size; @@ -103,15 +103,15 @@ struct HeaderDetails short numberOfTracks = 0; }; -static Optional parseMidiHeader (const uint8* const initialData, - const size_t maxSize) +static std::optional parseMidiHeader (const uint8* const initialData, + const size_t maxSize) { auto* data = initialData; auto remaining = maxSize; auto ch = tryRead (data, remaining); - if (! ch.hasValue()) + if (! ch.has_value()) return {}; if (*ch != ByteOrder::bigEndianInt ("MThd")) @@ -124,7 +124,7 @@ static Optional parseMidiHeader (const uint8* const initialData, { ch = tryRead (data, remaining); - if (! ch.hasValue()) + if (! ch.has_value()) return {}; if (*ch == ByteOrder::bigEndianInt ("MThd")) @@ -141,22 +141,22 @@ static Optional parseMidiHeader (const uint8* const initialData, const auto bytesRemaining = tryRead (data, remaining); - if (! bytesRemaining.hasValue() || *bytesRemaining > remaining) + if (! bytesRemaining.has_value() || *bytesRemaining > remaining) return {}; const auto optFileType = tryRead (data, remaining); - if (! optFileType.hasValue() || 2 < *optFileType) + if (! optFileType.has_value() || 2 < *optFileType) return {}; const auto optNumTracks = tryRead (data, remaining); - if (! optNumTracks.hasValue() || (*optFileType == 0 && *optNumTracks != 1)) + if (! optNumTracks.has_value() || (*optFileType == 0 && *optNumTracks != 1)) return {}; const auto optTimeFormat = tryRead (data, remaining); - if (! optTimeFormat.hasValue()) + if (! optTimeFormat.has_value()) return {}; HeaderDetails result; @@ -403,7 +403,7 @@ bool MidiFile::readFrom (InputStream& sourceStream, const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size); - if (! optHeader.hasValue()) + if (! optHeader.has_value()) return false; const auto header = *optHeader; @@ -416,12 +416,12 @@ bool MidiFile::readFrom (InputStream& sourceStream, { const auto optChunkType = MidiFileHelpers::tryRead (d, size); - if (! optChunkType.hasValue()) + if (! optChunkType.has_value()) return false; const auto optChunkSize = MidiFileHelpers::tryRead (d, size); - if (! optChunkSize.hasValue()) + if (! optChunkSize.has_value()) return false; const auto chunkSize = *optChunkSize; diff --git a/modules/yup_audio_basics/midi/yup_MidiMessageSequence.cpp b/modules/yup_audio_basics/midi/yup_MidiMessageSequence.cpp index fb9ce8a47..9d7f88b0c 100644 --- a/modules/yup_audio_basics/midi/yup_MidiMessageSequence.cpp +++ b/modules/yup_audio_basics/midi/yup_MidiMessageSequence.cpp @@ -335,12 +335,12 @@ void MidiMessageSequence::deleteSysExMessages() //============================================================================== class OptionalPitchWheel { - Optional value; + std::optional value; public: void emit (int channel, Array& out) const { - if (value.hasValue()) + if (value.has_value()) out.add (MidiMessage::pitchWheel (channel, *value)); } @@ -352,13 +352,13 @@ class OptionalPitchWheel class OptionalControllerValues { - Optional values[128]; + std::optional values[128]; public: void emit (int channel, Array& out) const { for (auto it = std::begin (values); it != std::end (values); ++it) - if (it->hasValue()) + if (it->has_value()) out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it)); } @@ -370,15 +370,15 @@ class OptionalControllerValues class OptionalProgramChange { - Optional value, bankLSB, bankMSB; + std::optional value, bankLSB, bankMSB; public: void emit (int channel, double time, Array& out) const { - if (! value.hasValue()) + if (! value.has_value()) return; - if (bankLSB.hasValue() && bankMSB.hasValue()) + if (bankLSB.has_value() && bankMSB.has_value()) { out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time)); out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time)); @@ -414,7 +414,7 @@ class ParameterNumberState nrpn }; - Optional newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb; + std::optional newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb; Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn; public: @@ -430,7 +430,7 @@ class ParameterNumberState auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb); const auto newest = std::tie (newestKind, newestMsb, newestLsb); - if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue()) + if (lastSent == newest || ! newestMsb.has_value() || ! newestLsb.has_value()) return; out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time)); diff --git a/modules/yup_audio_devices/native/yup_Audio_ios.cpp b/modules/yup_audio_devices/native/yup_Audio_ios.cpp index d5fd339b2..093597ed4 100644 --- a/modules/yup_audio_devices/native/yup_Audio_ios.cpp +++ b/modules/yup_audio_devices/native/yup_Audio_ios.cpp @@ -658,7 +658,7 @@ struct iOSAudioIODevice::Pimpl final : public AsyncUpdater impl.handleAudioTransportEvent (kAudioUnitRemoteControlEvent_Rewind); } - Optional getPosition() const override + std::optional getPosition() const override { if (! canControlTransportImpl()) return {}; diff --git a/modules/yup_audio_plugin_client/clap/yup_audio_plugin_client_CLAP.cpp b/modules/yup_audio_plugin_client/clap/yup_audio_plugin_client_CLAP.cpp index d39d4c799..bb24117d0 100644 --- a/modules/yup_audio_plugin_client/clap/yup_audio_plugin_client_CLAP.cpp +++ b/modules/yup_audio_plugin_client/clap/yup_audio_plugin_client_CLAP.cpp @@ -227,7 +227,7 @@ class AudioPluginPlayHeadCLAP final : public AudioPlayHead return; } - Optional getPosition() const override + std::optional getPosition() const override { if (process.transport == nullptr) return {}; diff --git a/modules/yup_core/containers/yup_Optional.h b/modules/yup_core/containers/yup_Optional.h deleted file mode 100644 index 67447e084..000000000 --- a/modules/yup_core/containers/yup_Optional.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - ============================================================================== - - This file is part of the YUP library. - Copyright (c) 2024 - kunitoki@gmail.com - - YUP is an open source library subject to open-source licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - to use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2022 - Raw Material Software Limited - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace yup -{ - -using Nullopt = std::nullopt_t; -constexpr auto nullopt = std::nullopt; - -// Without this, our tests can emit "unreachable code" warnings during -// link time code generation. -YUP_BEGIN_IGNORE_WARNINGS_MSVC (4702) - -#ifndef DOXYGEN -#define YUP_OPTIONAL_OPERATORS X (==) X (!=) X (<) X (<=) X (>) X (>=) -#endif - -/** - A simple optional type. - - In new code, you should probably prefer using std::optional directly. - - This is intended to stand-in for std::optional while YUP's minimum - supported language standard is lower than C++17. When the minimum language - standard moves to C++17, this class will probably be deprecated, in much - the same way that yup::ScopedPointer was deprecated in favour of - std::unique_ptr after C++11. - - This isn't really intended to be used by YUP clients. Instead, it's to be - used internally in YUP code, with an API close-enough to std::optional - that the types can be swapped with fairly minor disruption at some point in - the future, but *without breaking any public APIs*. - - @tags{Core} -*/ -template -class Optional -{ - template - struct IsOptional : std::false_type - { - }; - - template - struct IsOptional> : std::true_type - { - }; - -public: - Optional() = default; - Optional (const Optional&) = default; - Optional (Optional&&) = default; - Optional& operator= (const Optional&) = default; - Optional& operator= (Optional&&) = default; - - Optional (Nullopt) noexcept {} - - template >::value, int> = 0> - Optional (Head&& head, Tail&&... tail) noexcept (std::is_nothrow_constructible_v, Head, Tail...>) - : opt (std::forward (head), std::forward (tail)...) - { - } - - template - Optional (const Optional& other) noexcept (std::is_nothrow_constructible_v, const std::optional&>) - : opt (other.opt) - { - } - - template - Optional (Optional&& other) noexcept (std::is_nothrow_constructible_v, std::optional&&>) - : opt (std::move (other.opt)) - { - } - - template >::value, int> = 0> - Optional& operator= (Other&& other) noexcept (std::is_nothrow_assignable_v, Other>) - { - opt = std::forward (other); - return *this; - } - - template - Optional& operator= (const Optional& other) noexcept (std::is_nothrow_assignable_v, const std::optional&>) - { - opt = other.opt; - return *this; - } - - template - Optional& operator= (Optional&& other) noexcept (std::is_nothrow_assignable_v, std::optional&&>) - { - opt = std::move (other.opt); - return *this; - } - - template - auto& emplace (Other&&... other) - { - return opt.emplace (std::forward (other)...); - } - - void reset() noexcept - { - opt.reset(); - } - - void swap (Optional& other) noexcept (std::is_nothrow_swappable_v>) - { - opt.swap (other.opt); - } - - decltype (auto) operator->() { return opt.operator->(); } - - decltype (auto) operator->() const { return opt.operator->(); } - - decltype (auto) operator*() { return opt.operator*(); } - - decltype (auto) operator*() const { return opt.operator*(); } - - explicit operator bool() const noexcept { return opt.has_value(); } - - bool hasValue() const noexcept { return opt.has_value(); } - - template - decltype (auto) orFallback (U&& fallback) const& - { - return opt.value_or (std::forward (fallback)); - } - - template - decltype (auto) orFallback (U&& fallback) & - { - return opt.value_or (std::forward (fallback)); - } - -#define X(op) \ - template \ - friend bool operator op (const Optional&, const Optional&); \ - template \ - friend bool operator op (const Optional&, Nullopt); \ - template \ - friend bool operator op (Nullopt, const Optional&); \ - template \ - friend bool operator op (const Optional&, const U&); \ - template \ - friend bool operator op (const T&, const Optional&); - - YUP_OPTIONAL_OPERATORS - -#undef X - -private: - template - friend class Optional; - - std::optional opt; -}; - -YUP_END_IGNORE_WARNINGS_MSVC - -template -Optional> makeOptional (Value&& v) -{ - return std::forward (v); -} - -#ifndef DOXYGEN -#define X(op) \ - template \ - bool operator op (const Optional& lhs, const Optional& rhs) \ - { \ - return lhs.opt op rhs.opt; \ - } \ - template \ - bool operator op (const Optional& lhs, Nullopt rhs) \ - { \ - return lhs.opt op rhs; \ - } \ - template \ - bool operator op (Nullopt lhs, const Optional& rhs) \ - { \ - return lhs op rhs.opt; \ - } \ - template \ - bool operator op (const Optional& lhs, const U& rhs) \ - { \ - return lhs.opt op rhs; \ - } \ - template \ - bool operator op (const T& lhs, const Optional& rhs) \ - { \ - return lhs op rhs.opt; \ - } - -YUP_OPTIONAL_OPERATORS - -#undef X -#undef YUP_OPTIONAL_OPERATORS -#endif - -} // namespace yup diff --git a/modules/yup_core/containers/yup_Variant.cpp b/modules/yup_core/containers/yup_Variant.cpp index 599ebad4d..ad622af1e 100644 --- a/modules/yup_core/containers/yup_Variant.cpp +++ b/modules/yup_core/containers/yup_Variant.cpp @@ -1231,6 +1231,11 @@ var var::readFromStream (InputStream& input) return {}; } +var::NativeFunctionArgs::NativeFunctionArgs (const var& t) noexcept + : NativeFunctionArgs (t, nullptr, 0) +{ +} + var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept : thisObject (t) , arguments (args) diff --git a/modules/yup_core/containers/yup_Variant.h b/modules/yup_core/containers/yup_Variant.h index 30eb22877..344910361 100644 --- a/modules/yup_core/containers/yup_Variant.h +++ b/modules/yup_core/containers/yup_Variant.h @@ -64,6 +64,7 @@ class YUP_API var */ struct YUP_API NativeFunctionArgs { + NativeFunctionArgs (const var& thisObject) noexcept; NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; const var& thisObject; diff --git a/modules/yup_core/files/yup_File.cpp b/modules/yup_core/files/yup_File.cpp index f4adc2179..b6a09e858 100644 --- a/modules/yup_core/files/yup_File.cpp +++ b/modules/yup_core/files/yup_File.cpp @@ -803,6 +803,9 @@ bool File::startAsProcess (const String& parameters, const StringPairArray& envi //============================================================================== std::unique_ptr File::createInputStream() const { + if (isDirectory()) + return nullptr; + auto fin = std::make_unique (*this); if (fin->openedOk()) @@ -813,6 +816,9 @@ std::unique_ptr File::createInputStream() const std::unique_ptr File::createOutputStream (size_t bufferSize) const { + if (isDirectory()) + return nullptr; + auto fout = std::make_unique (*this, bufferSize); if (fout->openedOk()) diff --git a/modules/yup_core/files/yup_FileSearchPath.cpp b/modules/yup_core/files/yup_FileSearchPath.cpp index 2a61a05bb..9426c2be8 100644 --- a/modules/yup_core/files/yup_FileSearchPath.cpp +++ b/modules/yup_core/files/yup_FileSearchPath.cpp @@ -178,9 +178,10 @@ Array FileSearchPath::findChildFiles (int whatToLookFor, bool recurse, con int FileSearchPath::findChildFiles (Array& results, int whatToLookFor, bool recurse, const String& wildcard) const { int total = 0; + auto followSymlinks = recurse ? File::FollowSymlinks::noCycles : File::FollowSymlinks::yes; for (auto& d : directories) - total += File (d).findChildFiles (results, whatToLookFor, recurse, wildcard); + total += File (d).findChildFiles (results, whatToLookFor, recurse, wildcard, followSymlinks); return total; } diff --git a/modules/yup_core/maths/yup_Expression.cpp b/modules/yup_core/maths/yup_Expression.cpp index 51ce10644..d39fbd4d4 100644 --- a/modules/yup_core/maths/yup_Expression.cpp +++ b/modules/yup_core/maths/yup_Expression.cpp @@ -1094,9 +1094,14 @@ Expression::Expression (const String& stringToParse, String& parseError) Expression Expression::parse (String::CharPointerType& stringToParse, String& parseError) { Helpers::Parser parser (stringToParse); - Expression e (parser.readUpToComma().get()); + + auto result = parser.readUpToComma(); parseError = parser.error; - return e; + + if (result) + return Expression (result.get()); + + return {}; } double Expression::evaluate() const @@ -1114,7 +1119,9 @@ double Expression::evaluate (const Scope& scope, String& evaluationError) const { try { - return term->resolve (scope, 0)->toDouble(); + if (term != nullptr) + if (auto result = term->resolve (scope, 0)) + return result->toDouble(); } catch (Helpers::EvaluationError& e) { diff --git a/modules/yup_core/native/yup_SharedCode_posix.h b/modules/yup_core/native/yup_SharedCode_posix.h index 84f10da0c..9a89db760 100644 --- a/modules/yup_core/native/yup_SharedCode_posix.h +++ b/modules/yup_core/native/yup_SharedCode_posix.h @@ -993,10 +993,10 @@ class PosixSchedulerPriority return { scheduler, param.sched_priority }; } - static PosixSchedulerPriority getNativeSchedulerAndPriority (const Optional& rt, + static PosixSchedulerPriority getNativeSchedulerAndPriority (const std::optional& rt, [[maybe_unused]] Thread::Priority prio) { - const auto isRealtime = rt.hasValue(); + const auto isRealtime = rt.has_value(); const auto priority = [&] { diff --git a/modules/yup_core/text/yup_CharacterFunctions.cpp b/modules/yup_core/text/yup_CharacterFunctions.cpp index 9e8b1b067..f3513a264 100644 --- a/modules/yup_core/text/yup_CharacterFunctions.cpp +++ b/modules/yup_core/text/yup_CharacterFunctions.cpp @@ -453,38 +453,6 @@ int CharacterFunctions::getHexDigitValue (const yup_wchar digit) noexcept return -1; } -double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept -{ - if (exponent == 0) - return value; - - if (exactlyEqual (value, 0.0)) - return 0; - - const bool negative = (exponent < 0); - - if (negative) - exponent = -exponent; - - double result = 1.0, power = 10.0; - - for (int bit = 1; exponent != 0; bit <<= 1) - { - if ((exponent & bit) != 0) - { - exponent ^= bit; - result *= power; - - if (exponent == 0) - break; - } - - power *= power; - } - - return negative ? (value / result) : (value * result); -} - yup_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint8 c) noexcept { if (c < 0x80 || c >= 0xa0) diff --git a/modules/yup_core/text/yup_CharacterFunctions.h b/modules/yup_core/text/yup_CharacterFunctions.h index f213680e3..ea22de309 100644 --- a/modules/yup_core/text/yup_CharacterFunctions.h +++ b/modules/yup_core/text/yup_CharacterFunctions.h @@ -789,9 +789,6 @@ class YUP_API CharacterFunctions return text; } - -private: - static double mulexp10 (double value, int exponent) noexcept; }; } // namespace yup diff --git a/modules/yup_core/yup_core.h b/modules/yup_core/yup_core.h index 647cc4b63..2605b8eaa 100644 --- a/modules/yup_core/yup_core.h +++ b/modules/yup_core/yup_core.h @@ -276,7 +276,6 @@ YUP_END_IGNORE_WARNINGS_MSVC #include "memory/yup_ReferenceCountedObject.h" #include "memory/yup_ScopedPointer.h" #include "memory/yup_OptionalScopedPointer.h" -#include "containers/yup_Optional.h" #include "containers/yup_Enumerate.h" #include "containers/yup_ScopedValueSetter.h" #include "memory/yup_Singleton.h" diff --git a/modules/yup_data_model/undo/yup_UndoManager.cpp b/modules/yup_data_model/undo/yup_UndoManager.cpp index 452d3d995..2c54c74ec 100644 --- a/modules/yup_data_model/undo/yup_UndoManager.cpp +++ b/modules/yup_data_model/undo/yup_UndoManager.cpp @@ -135,6 +135,14 @@ bool UndoManager::perform (UndoableAction::Ptr action) if (! isEnabled()) return false; + // Ensure timer is started if MessageManager is now available and we're enabled + if (actionGroupThreshold > RelativeTime() + && ! isTimerRunning() + && MessageManager::getInstanceWithoutCreating() != nullptr) + { + startTimer (static_cast (actionGroupThreshold.inMilliseconds())); + } + if (action->perform (UndoableActionState::Redo)) { if (currentTransaction == nullptr) @@ -248,7 +256,8 @@ void UndoManager::setEnabled (bool shouldBeEnabled) if (shouldBeEnabled) { - if (actionGroupThreshold > RelativeTime()) + // Only start timer if MessageManager exists (may not be available during early initialization) + if (actionGroupThreshold > RelativeTime() && MessageManager::getInstanceWithoutCreating() != nullptr) startTimer (static_cast (actionGroupThreshold.inMilliseconds())); } else diff --git a/modules/yup_graphics/fonts/yup_Font.cpp b/modules/yup_graphics/fonts/yup_Font.cpp index 8590cc19c..f6cf5ed2f 100644 --- a/modules/yup_graphics/fonts/yup_Font.cpp +++ b/modules/yup_graphics/fonts/yup_Font.cpp @@ -28,10 +28,14 @@ namespace uint32_t axisTagFromString (StringRef tagName) { uint32_t tag = 0; - tag += static_cast (tagName[0]) << 24; - tag += static_cast (tagName[1]) << 16; - tag += static_cast (tagName[2]) << 8; - tag += static_cast (tagName[3]) << 0; + if (tagName.length() > 0) + tag += static_cast (tagName[0]) << 24; + if (tagName.length() > 1) + tag += static_cast (tagName[1]) << 16; + if (tagName.length() > 2) + tag += static_cast (tagName[2]) << 8; + if (tagName.length() > 3) + tag += static_cast (tagName[3]) << 0; return tag; } @@ -65,6 +69,9 @@ Font::Font (rive::rcp font, float height) Result Font::loadFromData (const MemoryBlock& fontBytes) { + if (fontBytes.isEmpty()) + return Result::fail ("Unable to instantiate font from empty data"); + font = HBFont::Decode (rive::make_span (static_cast (fontBytes.getData()), fontBytes.getSize())); return font ? Result::ok() : Result::fail ("Unable to load font"); } @@ -161,8 +168,6 @@ std::optional Font::getAxisDescription (int index) const std::optional Font::getAxisDescription (StringRef tagName) const { - jassert (tagName.length() == 4); - if (font == nullptr) return std::nullopt; @@ -221,8 +226,6 @@ void Font::setAxisValue (int index, float value) void Font::setAxisValue (StringRef tagName, float value) { - jassert (tagName.length() == 4); - if (font == nullptr) return; @@ -252,8 +255,6 @@ Font Font::withAxisValue (int index, float value) const Font Font::withAxisValue (StringRef tagName, float value) const { - jassert (tagName.length() == 4); - if (font == nullptr) return {}; @@ -267,9 +268,7 @@ Font Font::withAxisValue (StringRef tagName, float value) const void Font::setAxisValues (std::initializer_list axisOptions) { - jassert (axisOptions.size() > 0); - - if (font == nullptr) + if (font == nullptr || axisOptions.size() == 0) return; std::vector coords; @@ -295,9 +294,7 @@ void Font::setAxisValues (std::initializer_list axisOptions) Font Font::withAxisValues (std::initializer_list axisOptions) const { - jassert (axisOptions.size() > 0); - - if (font == nullptr) + if (font == nullptr || axisOptions.size() == 0) return {}; std::vector coords; @@ -331,8 +328,6 @@ void Font::resetAxisValue (int index) void Font::resetAxisValue (StringRef tagName) { - jassert (tagName.length() == 4); - if (font == nullptr) return; diff --git a/modules/yup_python/pybind11/attr.h b/modules/yup_python/pybind11/attr.h index 1044db94d..725cada27 100644 --- a/modules/yup_python/pybind11/attr.h +++ b/modules/yup_python/pybind11/attr.h @@ -12,84 +12,134 @@ #include "detail/common.h" #include "cast.h" +#include "trampoline_self_life_support.h" #include -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE) /// \addtogroup annotations /// @{ /// Annotation for methods -struct is_method { +struct is_method +{ handle class_; - explicit is_method(const handle &c) : class_(c) {} + + explicit is_method (const handle& c) + : class_ (c) + { + } }; /// Annotation for setters -struct is_setter {}; +struct is_setter +{ +}; /// Annotation for operators -struct is_operator {}; +struct is_operator +{ +}; /// Annotation for classes that cannot be subclassed -struct is_final {}; +struct is_final +{ +}; /// Annotation for parent scope -struct scope { +struct scope +{ handle value; - explicit scope(const handle &s) : value(s) {} + + explicit scope (const handle& s) + : value (s) + { + } }; /// Annotation for documentation -struct doc { - const char *value; - explicit doc(const char *value) : value(value) {} +struct doc +{ + const char* value; + + explicit doc (const char* value) + : value (value) + { + } }; /// Annotation for function names -struct name { - const char *value; - explicit name(const char *value) : value(value) {} +struct name +{ + const char* value; + + explicit name (const char* value) + : value (value) + { + } }; /// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { +struct sibling +{ handle value; - explicit sibling(const handle &value) : value(value.ptr()) {} + + explicit sibling (const handle& value) + : value (value.ptr()) + { + } }; /// Annotation indicating that a class derives from another given type template -struct base { - - PYBIND11_DEPRECATED( +struct base +{ + PYBIND11_DEPRECATED ( "base() was deprecated in favor of specifying 'T' as a template argument to class_") base() = default; }; /// Keep patient alive while nurse lives template -struct keep_alive {}; +struct keep_alive +{ +}; /// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance {}; +struct multiple_inheritance +{ +}; /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr {}; +struct dynamic_attr +{ +}; /// Annotation which enables the buffer protocol for a type -struct buffer_protocol {}; +struct buffer_protocol +{ +}; + +/// Annotation which enables releasing the GIL before calling the C++ destructor of wrapped +/// instances (pybind/pybind11#1446). +struct release_gil_before_calling_cpp_dtor +{ +}; /// Annotation which requests that a special metaclass is created for a type -struct metaclass { +struct metaclass +{ handle value; - PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + PYBIND11_DEPRECATED ("py::metaclass() is no longer required. It's turned on by default now.") metaclass() = default; /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) {} + explicit metaclass (handle value) + : value (value) + { + } }; /// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that @@ -101,25 +151,38 @@ struct metaclass { /// work with later versions of pybind11. You may wish to consult the /// implementation of `make_new_python_type` in `detail/classes.h` to understand /// the context in which the callback will be run. -struct custom_type_setup { - using callback = std::function; +struct custom_type_setup +{ + using callback = std::function; - explicit custom_type_setup(callback value) : value(std::move(value)) {} + explicit custom_type_setup (callback value) + : value (std::move (value)) + { + } callback value; }; /// Annotation that marks a class as local to the module: -struct module_local { +struct module_local +{ const bool value; - constexpr explicit module_local(bool v = true) : value(v) {} + + constexpr explicit module_local (bool v = true) + : value (v) + { + } }; /// Annotation to mark enums as an arithmetic type -struct arithmetic {}; +struct arithmetic +{ +}; /// Mark a function for addition at the beginning of the existing overload chain instead of the end -struct prepend {}; +struct prepend +{ +}; /** \rst A call policy which places one or more guard variables (``Ts...``) around the function call. @@ -143,77 +206,99 @@ template struct call_guard; template <> -struct call_guard<> { +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"); +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{}; +struct call_guard +{ + struct type + { + T guard {}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next {}; }; }; /// @} annotations -PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN (detail) /* Forward declarations */ enum op_id : int; enum op_type : int; struct undefined_t; template struct op_; -void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); +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 +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) {} + 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 { +#define PYBIND11_DETAIL_FUNCTION_RECORD_ABI_ID "v1" // PLEASE UPDATE if the struct is changed. + +struct function_record +{ function_record() - : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), is_method(false), is_setter(false), has_args(false), - has_kwargs(false), prepend(false) {} + : is_constructor (false) + , is_new_style_constructor (false) + , is_stateless (false) + , is_operator (false) + , is_method (false) + , is_setter (false) + , has_args (false) + , has_kwargs (false) + , prepend (false) + { + } /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + char* name = nullptr; /* why no C++ strings? They generate heavier code.. */ // User-specified documentation string - char *doc = nullptr; + char* doc = nullptr; /// Human-readable version of the function signature - char *signature = nullptr; + 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; + handle (*impl) (function_call&) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = {}; + void* data[3] = {}; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data)(function_record *ptr) = nullptr; + void (*free_data) (function_record* ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; @@ -256,7 +341,7 @@ struct function_record { std::uint16_t nargs_pos_only = 0; /// Python method object - PyMethodDef *def = nullptr; + PyMethodDef* def = nullptr; /// Python handle to the parent scope (a class or a module) handle scope; @@ -265,23 +350,37 @@ struct function_record { handle sibling; /// Pointer to next overload - function_record *next = nullptr; + function_record* next = nullptr; }; +// The main purpose of this macro is to make it easy to pin-point the critically related code +// sections. +#define PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS(...) \ + static_assert ( \ + __VA_ARGS__, \ + "Violation of precondition for pybind11/functional.h performance optimizations!") + /// Special data structure which (temporarily) holds metadata about a bound class -struct type_record { +struct type_record +{ PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false), is_final(false) {} + : multiple_inheritance (false) + , dynamic_attr (false) + , buffer_protocol (false) + , module_local (false) + , is_final (false) + , release_gil_before_calling_cpp_dtor (false) + { + } /// Handle to the parent scope handle scope; /// Name of the class - const char *name = nullptr; + const char* name = nullptr; // Pointer to RTTI type_info data structure - const std::type_info *type = nullptr; + const std::type_info* type = nullptr; /// How large is the underlying C++ type? size_t type_size = 0; @@ -293,19 +392,27 @@ struct type_record { size_t holder_size = 0; /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = nullptr; + void* (*operator_new) (size_t) = nullptr; /// Function pointer to class_<..>::init_instance - void (*init_instance)(instance *, const void *) = nullptr; + void (*init_instance) (instance*, const void*) = nullptr; /// Function pointer to class_<..>::dealloc - void (*dealloc)(detail::value_and_holder &) = nullptr; + void (*dealloc) (detail::value_and_holder&) = nullptr; + + /// Function pointer for casting alias class (aka trampoline) pointer to + /// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues + /// on platforms like macOS (see PR #5728 for details). + get_trampoline_self_life_support_fn get_trampoline_self_life_support = [] (void*) -> trampoline_self_life_support* + { + return nullptr; + }; /// List of base classes of the newly created type list bases; /// Optional docstring - const char *doc = nullptr; + const char* doc = nullptr; /// Custom metaclass (optional) handle metaclass; @@ -322,54 +429,68 @@ struct type_record { /// 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; /// Is the class inheritable from python classes? bool is_final : 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 + "\""); + /// Solves pybind/pybind11#1446 + bool release_gil_before_calling_cpp_dtor : 1; + + holder_enum_t holder_enum_v = holder_enum_t::undefined; + + 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")); + // SMART_HOLDER_BAKEIN_FOLLOW_ON: Refine holder compatibility checks. + bool this_has_unique_ptr_holder = (holder_enum_v == holder_enum_t::std_unique_ptr); + bool base_has_unique_ptr_holder = (base_info->holder_enum_v == holder_enum_t::std_unique_ptr); + if (this_has_unique_ptr_holder != base_has_unique_ptr_holder) + { + std::string tname (base.name()); + detail::clean_type_id (tname); + pybind11_fail ("generic_type: type \"" + std::string (name) + "\" " + + (this_has_unique_ptr_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_has_unique_ptr_holder ? "does not" : "does")); } - bases.append((PyObject *) base_info->type); + bases.append ((PyObject*) base_info->type); -#if PY_VERSION_HEX < 0x030B0000 +#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET dynamic_attr |= base_info->type->tp_dictoffset != 0; #else dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; #endif - if (caster) { - base_info->implicit_casts.emplace_back(type, caster); + if (caster) + { + base_info->implicit_casts.emplace_back (type, caster); } } }; -inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { - args.reserve(f.nargs); - args_convert.reserve(f.nargs); +inline function_call::function_call (const 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 {}; +struct is_new_style_constructor +{ +}; /** * Partial template specializations to process custom attributes provided to @@ -381,52 +502,67 @@ template struct process_attribute; template -struct process_attribute_default { +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) {} + 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); } +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); } +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 = d; } +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 = d; } }; + template <> -struct process_attribute : process_attribute {}; +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; } +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; } +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) { +struct process_attribute : process_attribute_default +{ + static void init (const is_method& s, function_record* r) + { r->is_method = true; r->scope = s.class_; } @@ -434,116 +570,144 @@ struct process_attribute : process_attribute_default { /// Process an attribute which indicates that this function is a setter template <> -struct process_attribute : process_attribute_default { - static void init(const is_setter &, function_record *r) { r->is_setter = true; } +struct process_attribute : process_attribute_default +{ + static void init (const is_setter&, function_record* r) { r->is_setter = true; } }; /// 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; } +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; } +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) { + : process_attribute_default +{ + static void init (const is_new_style_constructor&, function_record* r) + { r->is_new_style_constructor = true; } }; -inline void check_kw_only_arg(const arg &a, function_record *r) { - if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { - pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " - "args() argument"); +inline void check_kw_only_arg (const arg& a, function_record* r) +{ + if (r->args.size() > r->nargs_pos && (! a.name || a.name[0] == '\0')) + { + pybind11_fail ("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); } } -inline void append_self_arg_if_needed(function_record *r) { - if (r->is_method && r->args.empty()) { - r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); +inline void append_self_arg_if_needed (function_record* r) +{ + if (r->is_method && r->args.empty()) + { + r->args.emplace_back ("self", nullptr, handle(), /*convert=*/true, /*none=*/false); } } /// 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) { - append_self_arg_if_needed(r); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - - check_kw_only_arg(a, r); +struct process_attribute : process_attribute_default +{ + static void init (const arg& a, function_record* r) + { + append_self_arg_if_needed (r); + r->args.emplace_back (a.name, nullptr, handle(), ! a.flag_noconvert, a.flag_none); + + check_kw_only_arg (a, r); } }; /// 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( +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", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); } - if (!a.value) { + if (! a.value) + { #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) - std::string descr("'"); - if (a.name) { - descr += std::string(a.name) + ": "; + 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) + "'"; + 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) { + } + 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?)"); + 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?). " - "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " - "more information."); + pybind11_fail ("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "#define PYBIND11_DETAILED_ERROR_MESSAGES or 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); + r->args.emplace_back (a.name, a.descr, a.value.inc_ref(), ! a.flag_noconvert, a.flag_none); - check_kw_only_arg(a, r); + check_kw_only_arg (a, r); } }; /// Process a keyword-only-arguments-follow pseudo argument template <> -struct process_attribute : process_attribute_default { - static void init(const kw_only &, function_record *r) { - append_self_arg_if_needed(r); - if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { - pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " - "argument location (or omit kw_only() entirely)"); +struct process_attribute : process_attribute_default +{ + static void init (const kw_only&, function_record* r) + { + append_self_arg_if_needed (r); + if (r->has_args && r->nargs_pos != static_cast (r->args.size())) + { + pybind11_fail ("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); } - r->nargs_pos = static_cast(r->args.size()); + r->nargs_pos = static_cast (r->args.size()); } }; /// Process a positional-only-argument maker template <> -struct process_attribute : process_attribute_default { - static void init(const pos_only &, function_record *r) { - append_self_arg_if_needed(r); - r->nargs_pos_only = static_cast(r->args.size()); - if (r->nargs_pos_only > r->nargs_pos) { - pybind11_fail("pos_only(): cannot follow a py::args() argument"); +struct process_attribute : process_attribute_default +{ + static void init (const pos_only&, function_record* r) + { + append_self_arg_if_needed (r); + r->nargs_pos_only = static_cast (r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) + { + pybind11_fail ("pos_only(): cannot follow a py::args() argument"); } // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } @@ -553,68 +717,94 @@ struct process_attribute : process_attribute_default { /// that) template struct process_attribute::value>> - : process_attribute_default { - static void init(const handle &h, type_record *r) { r->bases.append(h); } + : 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); } +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) { +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; } +struct process_attribute : process_attribute_default +{ + static void init (const dynamic_attr&, type_record* r) { r->dynamic_attr = true; } }; template <> -struct process_attribute { - static void init(const custom_type_setup &value, type_record *r) { +struct process_attribute +{ + static void init (const custom_type_setup& value, type_record* r) + { r->custom_type_setup_callback = value.value; } }; template <> -struct process_attribute : process_attribute_default { - static void init(const is_final &, type_record *r) { r->is_final = true; } +struct process_attribute : process_attribute_default +{ + static void init (const is_final&, type_record* r) { r->is_final = true; } }; template <> -struct process_attribute : process_attribute_default { - static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +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; } +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; } +struct process_attribute : process_attribute_default +{ + static void init (const module_local& l, type_record* r) { r->module_local = l.value; } +}; + +template <> +struct process_attribute + : process_attribute_default +{ + static void init (const release_gil_before_calling_cpp_dtor&, type_record* r) + { + r->release_gil_before_calling_cpp_dtor = true; + } }; /// Process a 'prepend' attribute, putting this at the beginning of the overload chain template <> -struct process_attribute : process_attribute_default { - static void init(const prepend &, function_record *r) { r->prepend = true; } +struct process_attribute : process_attribute_default +{ + static void init (const prepend&, function_record* r) { r->prepend = true; } }; /// Process an 'arithmetic' attribute for enums (does nothing here) template <> -struct process_attribute : process_attribute_default {}; +struct process_attribute : process_attribute_default +{ +}; template -struct process_attribute> : process_attribute_default> {}; +struct process_attribute> : process_attribute_default> +{ +}; /** * Process a keep_alive call policy -- invokes keep_alive_impl during the @@ -623,50 +813,70 @@ struct process_attribute> : process_attribute_default struct process_attribute> - : public process_attribute_default> { + : public process_attribute_default> +{ template = 0> - static void precall(function_call &call) { - keep_alive_impl(Nurse, Patient, call, handle()); + static void precall (function_call& call) + { + keep_alive_impl (Nurse, Patient, call, handle()); } + template = 0> - static void postcall(function_call &, handle) {} + static void postcall (function_call&, handle) + { + } + template = 0> - static void precall(function_call &) {} + static void precall (function_call&) + { + } + template = 0> - static void postcall(function_call &call, handle ret) { - keep_alive_impl(Nurse, Patient, call, ret); + 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) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); +struct process_attributes +{ + static void init (const Args&... args, function_record* r) + { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100 (r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER (r); using expander = int[]; - (void) expander{ - 0, ((void) process_attribute::type>::init(args, r), 0)...}; + (void) expander { + 0, ((void) process_attribute::type>::init (args, r), 0)... + }; } - static void init(const Args &...args, type_record *r) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + + static void init (const Args&... args, type_record* r) + { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100 (r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER (r); using expander = int[]; - (void) expander{0, - (process_attribute::type>::init(args, r), 0)...}; + (void) expander { 0, + (process_attribute::type>::init (args, r), 0)... }; } - static void precall(function_call &call) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + + static void precall (function_call& call) + { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100 (call); using expander = int[]; - (void) expander{0, - (process_attribute::type>::precall(call), 0)...}; + (void) expander { 0, + (process_attribute::type>::precall (call), 0)... }; } - static void postcall(function_call &call, handle fn_ret) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); + + static void postcall (function_call& call, handle fn_ret) + { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100 (call, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER (fn_ret); using expander = int[]; - (void) expander{ - 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; + (void) expander { + 0, (process_attribute::type>::postcall (call, fn_ret), 0)... + }; } }; @@ -679,12 +889,13 @@ using extract_guard_t = typename exactly_one_t, Extr /// 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) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); - return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; + size_t named = constexpr_sum (std::is_base_of::value...), + size_t self = constexpr_sum (std::is_same::value...)> +constexpr bool expected_num_args (size_t nargs, bool has_args, bool has_kwargs) +{ + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100 (nargs, has_args, has_kwargs); + return named == 0 || (self + named + size_t (has_args) + size_t (has_kwargs)) == nargs; } -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END (detail) +PYBIND11_NAMESPACE_END (PYBIND11_NAMESPACE) diff --git a/modules/yup_python/pybind11/buffer_info.h b/modules/yup_python/pybind11/buffer_info.h index b99ee8bef..7a48fcc2e 100644 --- a/modules/yup_python/pybind11/buffer_info.h +++ b/modules/yup_python/pybind11/buffer_info.h @@ -11,16 +11,19 @@ #include "detail/common.h" -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN (detail) // Default, C-style strides -inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { +inline std::vector c_strides (const std::vector& shape, ssize_t itemsize) +{ auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - if (ndim > 0) { - for (size_t i = ndim - 1; i > 0; --i) { + std::vector strides (ndim, itemsize); + if (ndim > 0) + { + for (size_t i = ndim - 1; i > 0; --i) + { strides[i - 1] = strides[i] * shape[i]; } } @@ -28,10 +31,12 @@ inline std::vector c_strides(const std::vector &shape, ssize_t } // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` -inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { +inline 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) { + std::vector strides (ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + { strides[i] = strides[i - 1] * shape[i - 1]; } return strides; @@ -40,11 +45,12 @@ inline std::vector f_strides(const std::vector &shape, ssize_t template struct compare_buffer_info; -PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END (detail) /// Information record describing a Python buffer object -struct buffer_info { - void *ptr = nullptr; // Pointer to the underlying storage +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 @@ -57,101 +63,127 @@ struct buffer_info { buffer_info() = default; - buffer_info(void *ptr, - ssize_t itemsize, - const std::string &format, - ssize_t ndim, - detail::any_container shape_in, - detail::any_container strides_in, - bool readonly = false) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { - pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + buffer_info (void* ptr, + ssize_t itemsize, + const std::string& format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr (ptr) + , itemsize (itemsize) + , size (1) + , format (format) + , ndim (ndim) + , shape (std::move (shape_in)) + , strides (std::move (strides_in)) + , readonly (readonly) + { + 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) { + 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, - bool readonly = false) - : buffer_info(private_ctr_tag(), - ptr, - sizeof(T), - format_descriptor::format(), - static_cast(shape_in->size()), - std::move(shape_in), - std::move(strides_in), - readonly) {} - - buffer_info(void *ptr, - ssize_t itemsize, - const std::string &format, - ssize_t size, - bool readonly = false) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} + buffer_info (T* ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info (private_ctr_tag(), + ptr, + sizeof (T), + format_descriptor::format(), + static_cast (shape_in->size()), + std::move (shape_in), + std::move (strides_in), + readonly) + { + } + + buffer_info (void* ptr, + ssize_t itemsize, + const std::string& format, + ssize_t size, + bool readonly = false) + : buffer_info (ptr, itemsize, format, 1, { size }, { itemsize }, readonly) + { + } template - buffer_info(T *ptr, ssize_t size, bool readonly = false) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} + buffer_info (T* ptr, ssize_t size, bool readonly = false) + : buffer_info (ptr, sizeof (T), format_descriptor::format(), size, readonly) + { + } template - buffer_info(const T *ptr, ssize_t size, bool readonly = true) - : buffer_info( - const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} - - 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}, - /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects - * ignore this flag and return a view with NULL strides. - * When strides are NULL, build them manually. */ - view->strides - ? std::vector(view->strides, view->strides + view->ndim) - : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), - (view->readonly != 0)) { + buffer_info (const T* ptr, ssize_t size, bool readonly = true) + : buffer_info ( + const_cast (ptr), + sizeof (T), + format_descriptor::format(), + size, + readonly) + { + } + + 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 }, + /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects + * ignore this flag and return a view with NULL strides. + * When strides are NULL, build them manually. */ + view->strides + ? std::vector (view->strides, view->strides + view->ndim) + : detail::c_strides ({ view->shape, view->shape + view->ndim }, view->itemsize), + (view->readonly != 0)) + { // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->m_view = view; // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->ownview = ownview; } - buffer_info(const buffer_info &) = delete; - buffer_info &operator=(const buffer_info &) = delete; + buffer_info (const buffer_info&) = delete; + buffer_info& operator= (const buffer_info&) = delete; - buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } + buffer_info (buffer_info&& other) noexcept { (*this) = std::move (other); } - buffer_info &operator=(buffer_info &&rhs) noexcept { + buffer_info& operator= (buffer_info&& rhs) noexcept + { ptr = rhs.ptr; itemsize = rhs.itemsize; size = rhs.size; - format = std::move(rhs.format); + format = std::move (rhs.format); ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(m_view, rhs.m_view); - std::swap(ownview, rhs.ownview); + shape = std::move (rhs.shape); + strides = std::move (rhs.strides); + std::swap (m_view, rhs.m_view); + std::swap (ownview, rhs.ownview); readonly = rhs.readonly; return *this; } - ~buffer_info() { - if (m_view && ownview) { - PyBuffer_Release(m_view); + ~buffer_info() + { + if (m_view && ownview) + { + PyBuffer_Release (m_view); delete m_view; } } - Py_buffer *view() const { return m_view; } - Py_buffer *&view() { return m_view; } + Py_buffer* view() const { return m_view; } + + Py_buffer*& view() { return m_view; } /* True if the buffer item type is equivalent to `T`. */ // To define "equivalent" by example: @@ -160,49 +192,64 @@ struct buffer_info { // on some platforms, but `int` and `unsigned` will never be equivalent. // For the ground truth, please inspect `detail::compare_buffer_info<>`. template - bool item_type_is_equivalent_to() const { - return detail::compare_buffer_info::compare(*this); + bool item_type_is_equivalent_to() const + { + return detail::compare_buffer_info::compare (*this); } 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, - bool readonly) - : buffer_info( - ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} - - Py_buffer *m_view = nullptr; + 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, + bool readonly) + : buffer_info ( + ptr, + itemsize, + format, + ndim, + std::move (shape_in), + std::move (strides_in), + readonly) + { + } + + Py_buffer* m_view = nullptr; bool ownview = false; }; -PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN (detail) template -struct compare_buffer_info { - static bool compare(const buffer_info &b) { +struct compare_buffer_info +{ + static bool compare (const buffer_info& b) + { // NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *` - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + 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"))); +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"))); } }; -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END (detail) +PYBIND11_NAMESPACE_END (PYBIND11_NAMESPACE) diff --git a/modules/yup_python/pybind11/cast.h b/modules/yup_python/pybind11/cast.h index db3934118..9fc847c87 100644 --- a/modules/yup_python/pybind11/cast.h +++ b/modules/yup_python/pybind11/cast.h @@ -12,6 +12,7 @@ #include "detail/common.h" #include "detail/descr.h" +#include "detail/native_enum_data.h" #include "detail/type_caster_base.h" #include "detail/typeid.h" #include "pytypes.h" @@ -28,86 +29,224 @@ #include #include -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE) -PYBIND11_WARNING_DISABLE_MSVC(4127) +PYBIND11_WARNING_DISABLE_MSVC (4127) -PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN (detail) template -class type_caster : public type_caster_base {}; +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(); +typename make_caster::template cast_op_type cast_op (make_caster& caster) +{ + using result_t = typename make_caster::template cast_op_type; // See PR #4893 + return caster.operator result_t(); } + 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>(); + cast_op (make_caster&& caster) +{ + using result_t = typename make_caster::template cast_op_type< + typename std::add_rvalue_reference::type>; // See PR #4893 + return std::move (caster).operator result_t(); +} + +template +class type_caster_enum_type +{ +private: + using Underlying = typename std::underlying_type::type; + +public: + static constexpr auto name = const_name(); + + template + static handle cast (SrcType&& src, return_value_policy, handle parent) + { + handle native_enum = global_internals_native_enum_type_map_get_item (std::type_index (typeid (EnumType))); + if (native_enum) + { + return native_enum (static_cast (src)).release(); + } + return type_caster_base::cast ( + std::forward (src), + // Fixes https://github.com/pybind/pybind11/pull/3643#issuecomment-1022987818: + return_value_policy::copy, + parent); + } + + template + static handle cast (SrcType* src, return_value_policy policy, handle parent) + { + return cast (*src, policy, parent); + } + + bool load (handle src, bool convert) + { + handle native_enum = global_internals_native_enum_type_map_get_item (std::type_index (typeid (EnumType))); + if (native_enum) + { + if (! isinstance (src, native_enum)) + { + return false; + } + type_caster underlying_caster; + if (! underlying_caster.load (src.attr ("value"), convert)) + { + pybind11_fail ("native_enum internal consistency failure."); + } + value = static_cast (static_cast (underlying_caster)); + return true; + } + if (! pybind11_enum_) + { + pybind11_enum_.reset (new type_caster_base()); + } + return pybind11_enum_->load (src, convert); + } + + template + using cast_op_type = detail::cast_op_type; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator EnumType*() + { + if (! pybind11_enum_) + { + return &value; + } + return pybind11_enum_->operator EnumType*(); + } + + // NOLINTNEXTLINE(google-explicit-constructor) + operator EnumType&() + { + if (! pybind11_enum_) + { + return value; + } + return pybind11_enum_->operator EnumType&(); + } + +private: + std::unique_ptr> pybind11_enum_; + EnumType value; +}; + +template +struct type_caster_enum_type_enabled : std::true_type +{ +}; + +template +struct type_uses_type_caster_enum_type +{ + static constexpr bool value = std::is_enum::value && type_caster_enum_type_enabled::value; +}; + +template +class type_caster::value>> + : public type_caster_enum_type +{ +}; + +template ::value, int> = 0> +bool isinstance_native_enum_impl (handle obj, const std::type_info& tp) +{ + handle native_enum = global_internals_native_enum_type_map_get_item (tp); + if (! native_enum) + { + return false; + } + return isinstance (obj, native_enum); +} + +template ::value, int> = 0> +bool isinstance_native_enum_impl (handle, const std::type_info&) +{ + return false; +} + +template +bool isinstance_native_enum (handle obj, const std::type_info& tp) +{ + return isinstance_native_enum_impl> (obj, tp); } template -class type_caster> { +class type_caster> +{ private: using caster_t = make_caster; caster_t subcaster; - using reference_t = type &; + using reference_t = type&; using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert( - std::is_same::type &, subcaster_cast_op_type>::value + static_assert ( + std::is_same::type&, subcaster_cast_op_type>::value || std::is_same::value, "std::reference_wrapper caster requires T to have a caster with an " "`operator T &()` or `operator const T &()`"); public: - bool load(handle src, bool convert) { return subcaster.load(src, convert); } + bool load (handle src, bool convert) { return subcaster.load (src, convert); } + static constexpr auto name = caster_t::name; + static handle - cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + 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) + { policy = return_value_policy::automatic_reference; } - return caster_t::cast(&src.get(), policy, parent); + return caster_t::cast (&src.get(), policy, parent); } + template using cast_op_type = std::reference_wrapper; - explicit operator std::reference_wrapper() { return cast_op(subcaster); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ -protected: \ - type value; \ - \ -public: \ - static constexpr auto name = py_name; \ - template >::value, \ - int> \ - = 0> \ - static ::pybind11::handle cast( \ - T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ - if (!src) \ - return ::pybind11::none().release(); \ - if (policy == ::pybind11::return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); \ - delete src; \ - return h; \ - } \ - return cast(*src, policy, parent); \ - } \ - operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ - operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ - operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ - template \ + + explicit operator std::reference_wrapper() { return cast_op (subcaster); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ +protected: \ + type value; \ + \ +public: \ + static constexpr auto name = py_name; \ + template >::value, \ + int> = 0> \ + static ::pybind11::handle cast ( \ + T_* src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) \ + { \ + if (! src) \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) \ + { \ + auto h = cast (std::move (*src), policy, parent); \ + delete src; \ + return h; \ + } \ + return cast (*src, policy, parent); \ + } \ + operator type*() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type&() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type&&() && { return std::move (value); } /* NOLINT(bugprone-macro-parentheses) */ \ + template \ using cast_op_type = ::pybind11::detail::movable_cast_op_type template @@ -121,60 +260,84 @@ using is_std_char_type = any_of, /* std::string */ >; template -struct type_caster::value && !is_std_char_type::value>> { - using _py_type_0 = conditional_t; +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) { + bool load (handle src, bool convert) + { py_type py_value; - if (!src) { + if (! src) + { return false; } -#if !defined(PYPY_VERSION) - auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#if ! defined(PYPY_VERSION) + auto index_check = [] (PyObject* o) + { + return PyIndex_Check (o); + }; #else // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, // while CPython only considers the existence of `nb_index`/`__index__`. - auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; + auto index_check = [] (PyObject* o) + { + return hasattr (o, "__index__"); + }; #endif - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) { - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - } else { + 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()) - || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { + } + else if (PyFloat_Check (src.ptr()) + || (! convert && ! PYBIND11_LONG_CHECK (src.ptr()) && ! index_check (src.ptr()))) + { return false; - } else { + } + else + { handle src_or_index = src; // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. -#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) +#if defined(PYPY_VERSION) object index; - if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) - index = reinterpret_steal(PyNumber_Index(src.ptr())); - if (!index) { + if (! PYBIND11_LONG_CHECK (src.ptr())) + { // So: index_check(src.ptr()) + index = reinterpret_steal (PyNumber_Index (src.ptr())); + if (! index) + { PyErr_Clear(); - if (!convert) + if (! convert) return false; - } else { + } + else + { src_or_index = index; } } #endif - if (std::is_unsigned::value) { - py_value = as_unsigned(src_or_index.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src_or_index.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + if (std::is_unsigned::value) + { + py_value = as_unsigned (src_or_index.ptr()); + } + else + { // signed integer: + py_value = sizeof (T) <= sizeof (long) + ? (py_type) PyLong_AsLong (src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG (src_or_index.ptr()); } } @@ -184,15 +347,17 @@ struct type_caster::value && !is_std_char_t // Check to see if the conversion is valid (integers should match exactly) // Signed/unsigned checks happen elsewhere if (py_err - || (std::is_integral::value && sizeof(py_type) != sizeof(T) - && py_value != (py_type) (T) py_value)) { + || (std::is_integral::value && sizeof (py_type) != sizeof (T) + && py_value != (py_type) (T) py_value)) + { PyErr_Clear(); - if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); + if (py_err && convert && (PyNumber_Check (src.ptr()) != 0)) + { + auto tmp = reinterpret_steal (std::is_floating_point::value + ? PyNumber_Float (src.ptr()) + : PyNumber_Long (src.ptr())); PyErr_Clear(); - return load(tmp, false); + return load (tmp, false); } return false; } @@ -203,87 +368,111 @@ struct type_caster::value && !is_std_char_t template static typename std::enable_if::value, handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyFloat_FromDouble((double) src); + cast (U src, return_value_policy /* policy */, handle /* parent */) + { + return PyFloat_FromDouble ((double) src); } template - static typename std::enable_if::value && std::is_signed::value - && (sizeof(U) <= sizeof(long)), + static typename std::enable_if::value && std::is_signed::value + && (sizeof (U) <= sizeof (long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_SIGNED((long) src); + cast (U src, return_value_policy /* policy */, handle /* parent */) + { + return PYBIND11_LONG_FROM_SIGNED ((long) src); } template - static typename std::enable_if::value && std::is_unsigned::value - && (sizeof(U) <= sizeof(unsigned long)), + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof (U) <= sizeof (unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + cast (U src, return_value_policy /* policy */, handle /* parent */) + { + return PYBIND11_LONG_FROM_UNSIGNED ((unsigned long) src); } template - static typename std::enable_if::value && std::is_signed::value - && (sizeof(U) > sizeof(long)), + static typename std::enable_if::value && std::is_signed::value + && (sizeof (U) > sizeof (long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromLongLong((long long) src); + cast (U src, return_value_policy /* policy */, handle /* parent */) + { + return PyLong_FromLongLong ((long long) src); } template - static typename std::enable_if::value && std::is_unsigned::value - && (sizeof(U) > sizeof(unsigned long)), + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof (U) > sizeof (unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromUnsignedLongLong((unsigned long long) src); + cast (U src, return_value_policy /* policy */, handle /* parent */) + { + return PyLong_FromUnsignedLongLong ((unsigned long long) src); } - PYBIND11_TYPE_CASTER(T, const_name::value>("int", "float")); + PYBIND11_TYPE_CASTER (T, + io_name::value> ( + "typing.SupportsInt", + "int", + "typing.SupportsFloat", + "float")); }; template -struct void_caster { +struct void_caster +{ public: - bool load(handle src, bool) { - if (src && src.is_none()) { + bool load (handle src, bool) + { + if (src && src.is_none()) + { return true; } return false; } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + + static handle cast (T, return_value_policy /* policy */, handle /* parent */) + { return none().release(); } - PYBIND11_TYPE_CASTER(T, const_name("None")); + + PYBIND11_TYPE_CASTER (T, const_name ("None")); }; template <> -class type_caster : public void_caster {}; +class type_caster : public void_caster +{ +}; template <> -class type_caster : public type_caster { +class type_caster : public type_caster +{ public: using type_caster::cast; - bool load(handle h, bool) { - if (!h) { + bool load (handle h, bool) + { + if (! h) + { return false; } - if (h.is_none()) { + if (h.is_none()) + { value = nullptr; return true; } /* Check if this is a capsule */ - if (isinstance(h)) { - value = reinterpret_borrow(h); + if (isinstance (h)) + { + value = reinterpret_borrow (h); return true; } /* Check if this is a C++ type */ - const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + const auto& bases = all_type_info ((PyTypeObject*) type::handle_of (h).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; } @@ -291,62 +480,80 @@ class type_caster : public type_caster { return false; } - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) { - return capsule(ptr).release(); + static handle cast (const void* ptr, return_value_policy /* policy */, handle /* parent */) + { + if (ptr) + { + return capsule (ptr).release(); } return none().release(); } template - using cast_op_type = void *&; - explicit operator void *&() { return value; } - static constexpr auto name = const_name("capsule"); + using cast_op_type = void*&; + + explicit operator void*&() { return value; } + + static constexpr auto name = const_name (PYBIND11_CAPSULE_TYPE_TYPE_HINT); private: - void *value = nullptr; + void* value = nullptr; }; template <> -class type_caster : public void_caster {}; +class type_caster : public void_caster +{ +}; template <> -class type_caster { +class type_caster +{ public: - bool load(handle src, bool convert) { - if (!src) { + bool load (handle src, bool convert) + { + if (! src) + { return false; } - if (src.ptr() == Py_True) { + if (src.ptr() == Py_True) + { value = true; return true; } - if (src.ptr() == Py_False) { + if (src.ptr() == Py_False) + { value = false; return true; } - if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { - // (allow non-implicit conversion for numpy booleans) + if (convert || is_numpy_bool (src)) + { + // (allow non-implicit conversion for numpy booleans), use strncmp + // since NumPy 1.x had an additional trailing underscore. Py_ssize_t res = -1; - if (src.is_none()) { + if (src.is_none()) + { res = 0; // None is implicitly converted to False } #if defined(PYPY_VERSION) // On PyPy, check that "__bool__" attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) { - res = PyObject_IsTrue(src.ptr()); + 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()); + else if (auto* tp_as_number = Py_TYPE (src.ptr())->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) { + if (res == 0 || res == 1) + { value = (res != 0); return true; } @@ -354,115 +561,137 @@ class type_caster { } return false; } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { - return handle(src ? Py_True : Py_False).inc_ref(); + + static handle cast (bool src, return_value_policy /* policy */, handle /* parent */) + { + return handle (src ? Py_True : Py_False).inc_ref(); + } + + PYBIND11_TYPE_CASTER (bool, const_name ("bool")); + +private: + // Test if an object is a NumPy boolean (without fetching the type). + static inline bool is_numpy_bool (handle object) + { + const char* type_name = Py_TYPE (object.ptr())->tp_name; + // Name changed to `numpy.bool` in NumPy 2, `numpy.bool_` is needed for 1.x support + return std::strcmp ("numpy.bool", type_name) == 0 + || std::strcmp ("numpy.bool_", type_name) == 0; } - PYBIND11_TYPE_CASTER(bool, const_name("bool")); }; // Helper class for UTF-{8,16,32} C++ stl strings: template -struct string_caster { +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) == 1, + "Unsupported char size != 1"); #if defined(PYBIND11_HAS_U8STRING) - static_assert(!std::is_same::value || sizeof(CharT) == 1, - "Unsupported char8_t size != 1"); + static_assert (! std::is_same::value || sizeof (CharT) == 1, + "Unsupported char8_t size != 1"); #endif - 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"); + 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); + 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) { + bool load (handle src, bool) + { handle load_src = src; - if (!src) { + if (! src) + { return false; } - if (!PyUnicode_Check(load_src.ptr())) { - return load_raw(load_src); + if (! PyUnicode_Check (load_src.ptr())) + { + return load_raw (load_src); } // For UTF-8 we avoid the need for a temporary `bytes` object by using // `PyUnicode_AsUTF8AndSize`. - if (UTF_N == 8) { + if (UTF_N == 8) + { Py_ssize_t size = -1; - const auto *buffer - = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); - if (!buffer) { + const auto* buffer = reinterpret_cast (PyUnicode_AsUTF8AndSize (load_src.ptr(), &size)); + if (! buffer) + { PyErr_Clear(); return false; } - value = StringType(buffer, static_cast(size)); + value = StringType (buffer, static_cast (size)); return true; } - auto utfNbytes - = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), - UTF_N == 8 ? "utf-8" - : UTF_N == 16 ? "utf-16" - : "utf-32", - nullptr)); - if (!utfNbytes) { + auto 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 auto *buffer - = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); - size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + const auto* buffer = reinterpret_cast (PYBIND11_BYTES_AS_STRING (utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE (utfNbytes.ptr()) / sizeof (CharT); // Skip BOM for UTF-16/32 - if (UTF_N > 8) { + if (UTF_N > 8) + { buffer++; length--; } - value = StringType(buffer, length); + 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); + 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()); - auto nbytes = ssize_t(src.size() * sizeof(CharT)); - handle s = decode_utfN(buffer, nbytes); - if (!s) { + cast (const StringType& src, return_value_policy /* policy */, handle /* parent */) + { + const char* buffer = reinterpret_cast (src.data()); + auto 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, const_name(PYBIND11_STRING_NAME)); + PYBIND11_TYPE_CASTER (StringType, const_name (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); + 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 segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as // well), 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); + return PyUnicode_Decode (buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); #endif } @@ -470,25 +699,30 @@ struct string_caster { // 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_raw(enable_if_t::value, handle> src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { + bool load_raw (enable_if_t::value, handle> src) + { + if (PYBIND11_BYTES_CHECK (src.ptr())) + { // We were passed 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) { - pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); + const char* bytes = PYBIND11_BYTES_AS_STRING (src.ptr()); + if (! bytes) + { + pybind11_fail ("Unexpected PYBIND11_BYTES_AS_STRING() failure."); } - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + value = StringType (bytes, (size_t) PYBIND11_BYTES_SIZE (src.ptr())); return true; } - if (PyByteArray_Check(src.ptr())) { + if (PyByteArray_Check (src.ptr())) + { // We were passed a bytearray; accept it into a std::string or char* // without any encoding attempt. - const char *bytearray = PyByteArray_AsString(src.ptr()); - if (!bytearray) { - pybind11_fail("Unexpected PyByteArray_AsString() failure."); + const char* bytearray = PyByteArray_AsString (src.ptr()); + if (! bytearray) + { + pybind11_fail ("Unexpected PyByteArray_AsString() failure."); } - value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + value = StringType (bytearray, (size_t) PyByteArray_Size (src.ptr())); return true; } @@ -496,7 +730,8 @@ struct string_caster { } template - bool load_raw(enable_if_t::value, handle>) { + bool load_raw (enable_if_t::value, handle>) + { return false; } }; @@ -504,19 +739,24 @@ struct string_caster { template struct type_caster, enable_if_t::value>> - : string_caster> {}; + : string_caster> +{ +}; #ifdef PYBIND11_HAS_STRING_VIEW template struct type_caster, enable_if_t::value>> - : string_caster, true> {}; + : 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>> { +struct type_caster::value>> +{ using StringType = std::basic_string; using StringCaster = make_caster; StringCaster str_caster; @@ -524,51 +764,65 @@ struct type_caster::value>> { CharT one_char = 0; public: - bool load(handle src, bool convert) { - if (!src) { + bool load (handle src, bool convert) + { + if (! src) + { return false; } - if (src.is_none()) { + if (src.is_none()) + { // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) { + if (! convert) + { return false; } none = true; return true; } - return str_caster.load(src, convert); + return str_caster.load (src, convert); } - static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) { + static handle cast (const CharT* src, return_value_policy policy, handle parent) + { + if (src == nullptr) + { return pybind11::none().release(); } - return StringCaster::cast(StringType(src), policy, parent); + 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) { + 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); + return StringCaster::cast (StringType (1, src), policy, parent); } - explicit operator CharT *() { - return none ? nullptr : const_cast(static_cast(str_caster).c_str()); + explicit operator CharT*() + { + return none ? nullptr : const_cast (static_cast (str_caster).c_str()); } - explicit operator CharT &() { - if (none) { - throw value_error("Cannot convert None to a character"); + + explicit operator CharT&() + { + if (none) + { + throw value_error ("Cannot convert None to a character"); } - auto &value = static_cast(str_caster); + 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 (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 @@ -576,120 +830,143 @@ struct type_caster::value>> { // 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) { - auto v0 = static_cast(value[0]); + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) + { + auto v0 = static_cast (value[0]); // low bits only: 0-127 // 0b110xxxxx - start of 2-byte sequence // 0b1110xxxx - start of 3-byte sequence // 0b11110xxx - start of 4-byte sequence - size_t char0_bytes = (v0 & 0x80) == 0 ? 1 - : (v0 & 0xE0) == 0xC0 ? 2 - : (v0 & 0xF0) == 0xE0 ? 3 - : 4; + size_t char0_bytes = (v0 & 0x80) == 0 ? 1 + : (v0 & 0xE0) == 0xC0 ? 2 + : (v0 & 0xF0) == 0xE0 ? 3 + : 4; - if (char0_bytes == str_len) { + 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 - one_char = static_cast(((v0 & 3) << 6) - + (static_cast(value[1]) & 0x3F)); + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) + { // 0x110000xx 0x10xxxxxx + one_char = static_cast (((v0 & 3) << 6) + + (static_cast (value[1]) & 0x3F)); return one_char; } // Otherwise we have a single character, but it's > U+00FF - throw value_error("Character code point not in range(0x100)"); + 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) { - one_char = static_cast(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) { - throw value_error("Character code point not in range(0x10000)"); + else if (StringCaster::UTF_N == 16 && str_len == 2) + { + one_char = static_cast (value[0]); + if (one_char >= 0xD800 && one_char < 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"); + if (str_len != 1) + { + throw value_error ("Expected a character, but multi-character string found"); } one_char = value[0]; return one_char; } - static constexpr auto name = const_name(PYBIND11_STRING_NAME); + static constexpr auto name = const_name (PYBIND11_STRING_NAME); template using cast_op_type = pybind11::detail::cast_op_type<_T>; }; // Base implementation for std::tuple and std::pair template