diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..53e4058e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vcpkg"] + path = vcpkg + url = git@github.com:microsoft/vcpkg.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 37e25c16..e154a2a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,67 @@ cmake_minimum_required(VERSION 3.12.0) -project(SignalFlow C CXX) -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Develop) -endif() - -#------------------------------------------------------------------------------- -# Use C++11 -#------------------------------------------------------------------------------- -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_MACOSX_RPATH 1) #------------------------------------------------------------------------------- # By default, build a universal library on Apple. # For a faster build, select a single architecture by defining the # environmental variable CMAKE_OSX_ARCHITECTURES. +# Must be set prior to `project()` #------------------------------------------------------------------------------- if (NOT CMAKE_OSX_ARCHITECTURES) - set(CMAKE_OSX_ARCHITECTURES "${ARCHS_STANDARD}") + set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") endif() +#------------------------------------------------------------------------------- +# Build features +#------------------------------------------------------------------------------- +option(CMAKE_BUILD_SOUNDIO "Audio Hardware I/O support" ON) +if (CMAKE_BUILD_SOUNDIO) + list(APPEND VCPKG_MANIFEST_FEATURES "soundio") +endif () + +option(CMAKE_BUILD_FILEIO "Audio File I/O support" ON) +if (CMAKE_BUILD_FILEIO) + list(APPEND VCPKG_MANIFEST_FEATURES "fileio") +endif () + +option(CMAKE_BUILD_PYTHON "Build Python bindings" OFF) +if (CMAKE_BUILD_PYTHON) + list(APPEND VCPKG_MANIFEST_FEATURES "python") +endif () + +option(CMAKE_BUILD_VAMP "Vamp plugin support" OFF) +if (CMAKE_BUILD_VAMP) + list(APPEND VCPKG_MANIFEST_FEATURES "vamp") +endif () + +option(BUILD_EXAMPLES "Build examples" OFF) +if (BUILD_EXAMPLES) + list(APPEND VCPKG_MANIFEST_FEATURES "examples") +endif () + + +if (VCPKG_MANIFEST_FEATURES) + message("Building with features:") + foreach (feature IN LISTS VCPKG_MANIFEST_FEATURES) + message(" - ${feature}") + endforeach () +else () + message("Building without any extra features") +endif () + +set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" + CACHE STRING "Vcpkg toolchain file") + +#------------------------------------------------------------------------------- +# Project +#------------------------------------------------------------------------------- +project(SignalFlow C CXX) + +#------------------------------------------------------------------------------- +# Use C++11 +#------------------------------------------------------------------------------- +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_MACOSX_RPATH 1) + #------------------------------------------------------------------------------- # Shared compiler flags. #------------------------------------------------------------------------------- @@ -41,10 +84,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") endif() include_directories( - /usr/local/include source/include - source/lib - source/lib/pybind11/include ) if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") @@ -64,7 +104,14 @@ endif() #------------------------------------------------------------------------------- set(SRC) add_subdirectory("source/src") -set(SRC ${SRC} source/lib/json11/json11.cpp) + +#------------------------------------------------------------------------------- +# Build C++ examples +#------------------------------------------------------------------------------- +if(BUILD_EXAMPLES) + message("Building signalflow examples") + add_subdirectory("examples/cpp") +endif(BUILD_EXAMPLES) #------------------------------------------------------------------------------- # For builds of the Python bindings, add Python dependencies and flags @@ -90,42 +137,25 @@ add_library(signalflow SHARED ${SRC}) # Dependencies #------------------------------------------------------------------------------- -set(SOUNDIO_BUILD_DIR "" CACHE PATH "Path to built SoundIO library (will use find_library if blank)") -if (SOUNDIO_BUILD_DIR) - set(SOUNDIO_INCLUDE_DIR "${SOUNDIO_BUILD_DIR}/.." CACHE PATH "Path to SoundIO include directory (ignored if SOUNDIO_BUILD_DIR is blank") - add_definitions(-DHAVE_SOUNDIO) - target_link_libraries(signalflow "${SOUNDIO_BUILD_DIR}/$/soundio.lib") - include_directories(signalflow "${SOUNDIO_BUILD_DIR}/$/") - include_directories(signalflow "${SOUNDIO_INCLUDE_DIR}/") -else() - find_library(SOUNDIO soundio) - if (SOUNDIO) - message("Found libsoundio") +if (CMAKE_BUILD_SOUNDIO) + find_package(libsoundio CONFIG REQUIRED) + if (libsoundio_FOUND) add_definitions(-DHAVE_SOUNDIO) - target_link_libraries(signalflow ${SOUNDIO}) - else() - message(SEND_ERROR "Couldn't find libsoundio") - endif() -endif() - - -set(SNDFILE_BUILD_DIR "" CACHE PATH "Path to build sndfile library (will use find_library if blank)") -if (SNDFILE_BUILD_DIR) - set(SNDFILE_INCLUDE_DIR "${SNDFILE_BUILD_DIR}/../include" CACHE PATH "Path to sndfile include directory (ignored if SNDFILE_BUILD_DIR is blank") - add_definitions(-DHAVE_SNDFILE) - target_link_libraries(signalflow "${SNDFILE_BUILD_DIR}/$/sndfile.lib") - include_directories(signalflow "${SNDFILE_BUILD_DIR}/include/") - include_directories(signalflow "${SNDFILE_INCLUDE_DIR}/") -else() - find_library(SNDFILE sndfile) - if (SNDFILE) - message("Found sndfile") + target_link_libraries(signalflow libsoundio::libsoundio libsoundio::libsoundio_static) + else () + message(WARNING "could not find libsoundio") + endif () +endif () + +if (CMAKE_BUILD_FILEIO) + find_package(SndFile CONFIG REQUIRED) + if (SndFile_FOUND) add_definitions(-DHAVE_SNDFILE) - target_link_libraries(signalflow ${SNDFILE}) - else() - message(SEND_ERROR "Couldn't find libsndfile") - endif() -endif() + target_link_libraries(signalflow SndFile::sndfile) + else () + message(WARNING "could not find libsndfile") + endif () +endif () if (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(FFTW_BUILD_DIR "" CACHE PATH "Path to prebuilt FFTW library (will use find_library if blank)") @@ -138,27 +168,48 @@ if (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") "${FFTW_BUILD_DIR}/libfftw3l-3.lib" ) else() - find_library(FFTW3F fftw3f) - if (FFTW3F) - message("Found fftw3f") - target_link_libraries(signalflow ${FFTW3F}) + find_package(FFTW3f CONFIG REQUIRED) + target_link_libraries(signalflow PRIVATE FFTW3::fftw3f) + if (FFTW3f_FOUND) add_definitions(-DFFT_FFTW) - else() - # if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(FATAL_ERROR "Couldn't find fftw3f") - # endif() - endif() + endif () endif() endif(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") -find_library(VAMP vamp-hostsdk) -if (VAMP) - message("Found vamp") - add_definitions(-DHAVE_VAMP) - target_link_libraries(signalflow ${VAMP}) +if (CMAKE_BUILD_VAMP) + find_library(VAMP vamp-hostsdk) + if (VAMP) + message("Found vamp") + add_definitions(-DHAVE_VAMP) + target_link_libraries(signalflow ${VAMP}) + else () + message(WARNING "Couldn't find vamp") + endif () +endif () + +find_package(JSON11 CONFIG REQUIRED) +if (JSON11_FOUND) + target_link_libraries(signalflow ${JSON11_LIBRARIES}) + target_include_directories(signalflow PRIVATE ${JSON11_INCLUDE_DIRS}) else() - message(WARNING "Couldn't find vamp") -endif() + message(WARNING "Couldn't find json11") +endif () + +if (CMAKE_BUILD_PYTHON) + find_package(Python3 COMPONENTS Development REQUIRED) + if (python3_FOUND) + target_link_libraries(signalflow Python3::Python) + else() + message(WARNING "Couldn't find python3") + endif () + + find_package(pybind11 CONFIG REQUIRED) + if (pybind11_FOUND) + target_link_libraries(signalflow pybind11::lto pybind11::embed pybind11::module) + else() + message(WARNING "Couldn't find pybind11") + endif () +endif () #------------------------------------------------------------------------------- @@ -173,11 +224,6 @@ endif() if (CMAKE_BUILD_PYTHON) target_link_libraries(signalflow ${PYTHON_LIBRARY}) -else() - #------------------------------------------------------------------------------- - # Build examples - #------------------------------------------------------------------------------- - add_subdirectory("examples/cpp") endif() #------------------------------------------------------------------------------- diff --git a/README.md b/README.md index daac3409..c259b05c 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,12 @@ graph.wait() ### macOS To install the Python library on macOS, install dependencies with homebrew: -``` -brew install cmake python libsndfile libsoundio +```sh +brew install cmake python ``` Clone this repository, then build and install with `pip`: -``` +```sh pip3 install . ``` @@ -58,18 +58,18 @@ pip3 install . ### Linux, Raspberry Pi To install the Python library on Linux, install dependencies with apt: -``` +```sh apt-get install git cmake g++ python3-pip libasound2-dev libsndfile1-dev libsoundio-dev fftw3-dev ``` If you experience an error on Raspberry Pi `libf77blas.so.3: cannot open shared object file`: -``` +```sh sudo apt-get install libatlas-base-dev ``` Clone this repository, then build and install with `pip`: -``` +```sh pip3 install . ``` @@ -91,7 +91,22 @@ As of 2021-03-03, only the signalflow project has been ported to build correctly ## Build (C++) -To build and install the C++ core without the Python binding layer: +### Dependencies + +Ensure the vcpkg submodule is checked out + +```sh +git submodule init +git submodule update +``` + +Run the vcpkg bootstrap script (you can leave out `-disableMetrics` if you want to send usage data to Microsoft) +```sh +cd vcpkg +./bootstrap-vcpkg.sh -disableMetrics +``` + +Build and install the C++ core without the Python binding layer: ``` mkdir build cd build diff --git a/examples/cpp/audio-in-example.cpp b/examples/cpp/audio-in-example.cpp index 02c5647b..d9e08118 100644 --- a/examples/cpp/audio-in-example.cpp +++ b/examples/cpp/audio-in-example.cpp @@ -13,6 +13,7 @@ using namespace signalflow; int main() { +#ifdef HAVE_SOUNDIO /*------------------------------------------------------------------------ * Instantiate a global processing graph. *-----------------------------------------------------------------------*/ @@ -51,4 +52,8 @@ int main() } graph->wait(); +#else + std::cerr << "signalflow was not built with libsoundio" << std::endl; + return 0; +#endif } diff --git a/examples/cpp/buffer-save-example.cpp b/examples/cpp/buffer-save-example.cpp index fcad4f3b..8c624a7d 100644 --- a/examples/cpp/buffer-save-example.cpp +++ b/examples/cpp/buffer-save-example.cpp @@ -13,6 +13,7 @@ using namespace signalflow; int main() { +#if defined(HAVE_SOUNDIO) && defined(HAVE_SNDFILE) /*------------------------------------------------------------------------ * Instantiate the global processing graph. *-----------------------------------------------------------------------*/ @@ -49,4 +50,9 @@ int main() std::cout << "Finished recording." << std::endl; buffer->save("out.wav"); + +#else + std::cerr << "signalflow was not built with libsoundio and/or libsndfile" << std::endl; + return 0; +#endif } diff --git a/examples/cpp/granulator-example.cpp b/examples/cpp/granulator-example.cpp index e3ec5725..c2cd1aba 100644 --- a/examples/cpp/granulator-example.cpp +++ b/examples/cpp/granulator-example.cpp @@ -14,6 +14,8 @@ using namespace signalflow; int main() { +#ifdef HAVE_SNDFILE + /*------------------------------------------------------------------------ * Instantiate a single AudioGraph object for all global audio processing. *-----------------------------------------------------------------------*/ @@ -25,7 +27,7 @@ int main() BufferRef buffer = new Buffer("audio/gliss.aif"); /*------------------------------------------------------------------------ - * RandomImpulse creates an impulse train with randomised interval, at a + * RandomImpulse creates an impulse train with randomised interval, at a * given mean frequency. This is used to trigger grains. *-----------------------------------------------------------------------*/ NodeRef dust = new RandomImpulse(100.0); @@ -57,4 +59,9 @@ int main() * Loop forever. *-----------------------------------------------------------------------*/ graph->wait(); + +#else + std::cerr << "signalflow was not built with libsndfile" << std::endl; + return 0; +#endif } diff --git a/examples/cpp/mouse-control-example.cpp b/examples/cpp/mouse-control-example.cpp index 20703ec1..fb2220ff 100644 --- a/examples/cpp/mouse-control-example.cpp +++ b/examples/cpp/mouse-control-example.cpp @@ -14,6 +14,7 @@ using namespace signalflow; int main() { +#ifdef HAVE_SNDFILE /*------------------------------------------------------------------------ * Instantiate a global processing graph. *-----------------------------------------------------------------------*/ @@ -48,4 +49,9 @@ int main() granulator->set_input("max_grains", 50); graph->play(granulator * 0.2); graph->wait(); + +#else + std::cerr << "signalflow was not built with libsndfile" << std::endl; + return 0; +#endif } diff --git a/examples/cpp/spooky-wobble.cpp b/examples/cpp/spooky-wobble.cpp index 49cf49a1..24321bec 100644 --- a/examples/cpp/spooky-wobble.cpp +++ b/examples/cpp/spooky-wobble.cpp @@ -10,6 +10,7 @@ using namespace signalflow; int main() { +#ifdef HAVE_SNDFILE AudioGraphRef graph = new AudioGraph(); /*------------------------------------------------------------------------ @@ -38,4 +39,9 @@ int main() * Loop indefinitely. *-----------------------------------------------------------------------*/ graph->wait(); + +#else + std::cerr << "signalflow was not built with libsndfile" << std::endl; + return 0; +#endif } diff --git a/setup.py b/setup.py index 16de2b92..74535954 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def build_extension(self, ext): build_args = ['--config', cfg, '-j', '2'] cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - '-DCMAKE_BUILD_PYTHON=1', + '-DCMAKE_BUILD_PYTHON=ON', '-DCMAKE_BUILD_TYPE=' + cfg] if 'CMAKE_OSX_ARCHITECTURES' in os.environ: cmake_args += ['-DCMAKE_OSX_ARCHITECTURES=%s' % os.environ['CMAKE_OSX_ARCHITECTURES']] diff --git a/source/include/signalflow/buffer/buffer.h b/source/include/signalflow/buffer/buffer.h index 80dc24d5..e6c3ffe4 100644 --- a/source/include/signalflow/buffer/buffer.h +++ b/source/include/signalflow/buffer/buffer.h @@ -77,12 +77,14 @@ class Buffer *------------------------------------------------------------------------*/ Buffer(std::vector data); +#ifdef HAVE_SNDFILE /**------------------------------------------------------------------------ * Load the contents of the audio file `filename` into a new buffer. * The file must be of a format that libsndfile can read. * *------------------------------------------------------------------------*/ Buffer(std::string filename); +#endif /**------------------------------------------------------------------------ * Destroy the buffer. @@ -96,6 +98,7 @@ class Buffer *------------------------------------------------------------------------*/ void resize(int num_channels, int num_frames); +#ifdef HAVE_SNDFILE /**------------------------------------------------------------------------ * Load the contents of `filename` into the buffer. * If the buffer is smaller than the file's contents, only the first @@ -115,6 +118,7 @@ class Buffer * *------------------------------------------------------------------------*/ void save(std::string filename); +#endif /**------------------------------------------------------------------------ * Splits the buffer into chunks of `num_frames_per_part` frames, @@ -228,6 +232,7 @@ class Buffer *------------------------------------------------------------------------*/ float get_duration(); +#ifdef HAVE_SNDFILE /**------------------------------------------------------------------------ * Get the filename that the buffer was loaded from / saved to, if set. * @@ -235,6 +240,7 @@ class Buffer * *------------------------------------------------------------------------*/ std::string get_filename(); +#endif /**------------------------------------------------------------------------ * Get a pointer to the buffer's data. @@ -275,7 +281,9 @@ class Buffer sample **data = nullptr; protected: +#ifdef HAVE_SNDFILE std::string filename; +#endif float sample_rate; unsigned int num_channels; unsigned long num_frames; diff --git a/source/include/signalflow/core/graph.h b/source/include/signalflow/core/graph.h index 00cc781a..9176ad1b 100644 --- a/source/include/signalflow/core/graph.h +++ b/source/include/signalflow/core/graph.h @@ -12,7 +12,9 @@ #include "signalflow/node/node.h" #include "signalflow/patch/patch.h" +#ifdef HAVE_SNDFILE #include +#endif namespace signalflow { @@ -272,6 +274,7 @@ class AudioGraph *--------------------------------------------------------------------------------*/ void stop(PatchRef patch); +#ifdef HAVE_SNDFILE /**-------------------------------------------------------------------------------- * Start recording the graph's output to a named file. * @@ -288,6 +291,7 @@ class AudioGraph * *--------------------------------------------------------------------------------*/ void stop_recording(); +#endif /**-------------------------------------------------------------------------------- * Get audio sample rate. @@ -392,9 +396,11 @@ class AudioGraph NodeRef output = nullptr; AudioGraphConfig config; +#ifdef HAVE_SNDFILE SNDFILE *recording_fd; float *recording_buffer; int recording_num_channels; +#endif friend class Buffer; }; diff --git a/source/include/signalflow/core/util.h b/source/include/signalflow/core/util.h index 59fc3148..041e93f1 100644 --- a/source/include/signalflow/core/util.h +++ b/source/include/signalflow/core/util.h @@ -35,7 +35,9 @@ float signalflow_db_to_amplitude(float db); float signalflow_amplitude_to_db(float amp); void signalflow_save_block_to_text_file(sample *block, int num_samples, std::string filename); +#ifdef HAVE_SNDFILE void signalflow_save_block_to_wav_file(sample *block, int num_samples, std::string filename); +#endif std::vector signalflow_binary_sequence_to_vector(std::string binary); diff --git a/source/lib/json11/LICENSE.txt b/source/lib/json11/LICENSE.txt deleted file mode 100644 index 691742e9..00000000 --- a/source/lib/json11/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013 Dropbox, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/source/lib/json11/json11.cpp b/source/lib/json11/json11.cpp deleted file mode 100644 index 9647846b..00000000 --- a/source/lib/json11/json11.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/* Copyright (c) 2013 Dropbox, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "json11.hpp" -#include -#include -#include -#include -#include - -namespace json11 { - -static const int max_depth = 200; - -using std::string; -using std::vector; -using std::map; -using std::make_shared; -using std::initializer_list; -using std::move; - -/* Helper for representing null - just a do-nothing struct, plus comparison - * operators so the helpers in JsonValue work. We can't use nullptr_t because - * it may not be orderable. - */ -struct NullStruct { - bool operator==(NullStruct) const { return true; } - bool operator<(NullStruct) const { return false; } -}; - -/* * * * * * * * * * * * * * * * * * * * - * Serialization - */ - -static void dump(NullStruct, string &out) { - out += "null"; -} - -static void dump(double value, string &out) { - if (std::isfinite(value)) { - char buf[32]; - snprintf(buf, sizeof buf, "%.17g", value); - out += buf; - } else { - out += "null"; - } -} - -static void dump(int value, string &out) { - char buf[32]; - snprintf(buf, sizeof buf, "%d", value); - out += buf; -} - -static void dump(bool value, string &out) { - out += value ? "true" : "false"; -} - -static void dump(const string &value, string &out) { - out += '"'; - for (size_t i = 0; i < value.length(); i++) { - const char ch = value[i]; - if (ch == '\\') { - out += "\\\\"; - } else if (ch == '"') { - out += "\\\""; - } else if (ch == '\b') { - out += "\\b"; - } else if (ch == '\f') { - out += "\\f"; - } else if (ch == '\n') { - out += "\\n"; - } else if (ch == '\r') { - out += "\\r"; - } else if (ch == '\t') { - out += "\\t"; - } else if (static_cast(ch) <= 0x1f) { - char buf[8]; - snprintf(buf, sizeof buf, "\\u%04x", ch); - out += buf; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 - && static_cast(value[i+2]) == 0xa8) { - out += "\\u2028"; - i += 2; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 - && static_cast(value[i+2]) == 0xa9) { - out += "\\u2029"; - i += 2; - } else { - out += ch; - } - } - out += '"'; -} - -static void dump(const Json::array &values, string &out) { - bool first = true; - out += "["; - for (const auto &value : values) { - if (!first) - out += ", "; - value.dump(out); - first = false; - } - out += "]"; -} - -static void dump(const Json::object &values, string &out) { - bool first = true; - out += "{"; - for (const auto &kv : values) { - if (!first) - out += ", "; - dump(kv.first, out); - out += ": "; - kv.second.dump(out); - first = false; - } - out += "}"; -} - -void Json::dump(string &out) const { - m_ptr->dump(out); -} - -/* * * * * * * * * * * * * * * * * * * * - * Value wrappers - */ - -template -class Value : public JsonValue { -protected: - - // Constructors - explicit Value(const T &value) : m_value(value) {} - explicit Value(T &&value) : m_value(move(value)) {} - - // Get type tag - Json::Type type() const override { - return tag; - } - - // Comparisons - bool equals(const JsonValue * other) const override { - return m_value == static_cast *>(other)->m_value; - } - bool less(const JsonValue * other) const override { - return m_value < static_cast *>(other)->m_value; - } - - const T m_value; - void dump(string &out) const override { json11::dump(m_value, out); } -}; - -class JsonDouble final : public Value { - double number_value() const override { return m_value; } - int int_value() const override { return static_cast(m_value); } - bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } - bool less(const JsonValue * other) const override { return m_value < other->number_value(); } -public: - explicit JsonDouble(double value) : Value(value) {} -}; - -class JsonInt final : public Value { - double number_value() const override { return m_value; } - int int_value() const override { return m_value; } - bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } - bool less(const JsonValue * other) const override { return m_value < other->number_value(); } -public: - explicit JsonInt(int value) : Value(value) {} -}; - -class JsonBoolean final : public Value { - bool bool_value() const override { return m_value; } -public: - explicit JsonBoolean(bool value) : Value(value) {} -}; - -class JsonString final : public Value { - const string &string_value() const override { return m_value; } -public: - explicit JsonString(const string &value) : Value(value) {} - explicit JsonString(string &&value) : Value(move(value)) {} -}; - -class JsonArray final : public Value { - const Json::array &array_items() const override { return m_value; } - const Json & operator[](size_t i) const override; -public: - explicit JsonArray(const Json::array &value) : Value(value) {} - explicit JsonArray(Json::array &&value) : Value(move(value)) {} -}; - -class JsonObject final : public Value { - const Json::object &object_items() const override { return m_value; } - const Json & operator[](const string &key) const override; -public: - explicit JsonObject(const Json::object &value) : Value(value) {} - explicit JsonObject(Json::object &&value) : Value(move(value)) {} -}; - -class JsonNull final : public Value { -public: - JsonNull() : Value({}) {} -}; - -/* * * * * * * * * * * * * * * * * * * * - * Static globals - static-init-safe - */ -struct Statics { - const std::shared_ptr null = make_shared(); - const std::shared_ptr t = make_shared(true); - const std::shared_ptr f = make_shared(false); - const string empty_string; - const vector empty_vector; - const map empty_map; - Statics() {} -}; - -static const Statics & statics() { - static const Statics s {}; - return s; -} - -static const Json & static_null() { - // This has to be separate, not in Statics, because Json() accesses statics().null. - static const Json json_null; - return json_null; -} - -/* * * * * * * * * * * * * * * * * * * * - * Constructors - */ - -Json::Json() noexcept : m_ptr(statics().null) {} -Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} -Json::Json(double value) : m_ptr(make_shared(value)) {} -Json::Json(int value) : m_ptr(make_shared(value)) {} -Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} -Json::Json(const string &value) : m_ptr(make_shared(value)) {} -Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} -Json::Json(const char * value) : m_ptr(make_shared(value)) {} -Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} -Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} -Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} -Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} - -/* * * * * * * * * * * * * * * * * * * * - * Accessors - */ - -Json::Type Json::type() const { return m_ptr->type(); } -double Json::number_value() const { return m_ptr->number_value(); } -int Json::int_value() const { return m_ptr->int_value(); } -bool Json::bool_value() const { return m_ptr->bool_value(); } -const string & Json::string_value() const { return m_ptr->string_value(); } -const vector & Json::array_items() const { return m_ptr->array_items(); } -const map & Json::object_items() const { return m_ptr->object_items(); } -const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } -const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } - -double JsonValue::number_value() const { return 0; } -int JsonValue::int_value() const { return 0; } -bool JsonValue::bool_value() const { return false; } -const string & JsonValue::string_value() const { return statics().empty_string; } -const vector & JsonValue::array_items() const { return statics().empty_vector; } -const map & JsonValue::object_items() const { return statics().empty_map; } -const Json & JsonValue::operator[] (size_t) const { return static_null(); } -const Json & JsonValue::operator[] (const string &) const { return static_null(); } - -const Json & JsonObject::operator[] (const string &key) const { - auto iter = m_value.find(key); - return (iter == m_value.end()) ? static_null() : iter->second; -} -const Json & JsonArray::operator[] (size_t i) const { - if (i >= m_value.size()) return static_null(); - else return m_value[i]; -} - -/* * * * * * * * * * * * * * * * * * * * - * Comparison - */ - -bool Json::operator== (const Json &other) const { - if (m_ptr == other.m_ptr) - return true; - if (m_ptr->type() != other.m_ptr->type()) - return false; - - return m_ptr->equals(other.m_ptr.get()); -} - -bool Json::operator< (const Json &other) const { - if (m_ptr == other.m_ptr) - return false; - if (m_ptr->type() != other.m_ptr->type()) - return m_ptr->type() < other.m_ptr->type(); - - return m_ptr->less(other.m_ptr.get()); -} - -/* * * * * * * * * * * * * * * * * * * * - * Parsing - */ - -/* esc(c) - * - * Format char c suitable for printing in an error message. - */ -static inline string esc(char c) { - char buf[12]; - if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { - snprintf(buf, sizeof buf, "'%c' (%d)", c, c); - } else { - snprintf(buf, sizeof buf, "(%d)", c); - } - return string(buf); -} - -static inline bool in_range(long x, long lower, long upper) { - return (x >= lower && x <= upper); -} - -namespace { -/* JsonParser - * - * Object that tracks all state of an in-progress parse. - */ -struct JsonParser final { - - /* State - */ - const string &str; - size_t i; - string &err; - bool failed; - const JsonParse strategy; - - /* fail(msg, err_ret = Json()) - * - * Mark this parse as failed. - */ - Json fail(string &&msg) { - return fail(move(msg), Json()); - } - - template - T fail(string &&msg, const T err_ret) { - if (!failed) - err = std::move(msg); - failed = true; - return err_ret; - } - - /* consume_whitespace() - * - * Advance until the current character is non-whitespace. - */ - void consume_whitespace() { - while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') - i++; - } - - /* consume_comment() - * - * Advance comments (c-style inline and multiline). - */ - bool consume_comment() { - bool comment_found = false; - if (str[i] == '/') { - i++; - if (i == str.size()) - return fail("unexpected end of input after start of comment", false); - if (str[i] == '/') { // inline comment - i++; - // advance until next line, or end of input - while (i < str.size() && str[i] != '\n') { - i++; - } - comment_found = true; - } - else if (str[i] == '*') { // multiline comment - i++; - if (i > str.size()-2) - return fail("unexpected end of input inside multi-line comment", false); - // advance until closing tokens - while (!(str[i] == '*' && str[i+1] == '/')) { - i++; - if (i > str.size()-2) - return fail( - "unexpected end of input inside multi-line comment", false); - } - i += 2; - comment_found = true; - } - else - return fail("malformed comment", false); - } - return comment_found; - } - - /* consume_garbage() - * - * Advance until the current character is non-whitespace and non-comment. - */ - void consume_garbage() { - consume_whitespace(); - if(strategy == JsonParse::COMMENTS) { - bool comment_found = false; - do { - comment_found = consume_comment(); - if (failed) return; - consume_whitespace(); - } - while(comment_found); - } - } - - /* get_next_token() - * - * Return the next non-whitespace character. If the end of the input is reached, - * flag an error and return 0. - */ - char get_next_token() { - consume_garbage(); - if (failed) return (char)0; - if (i == str.size()) - return fail("unexpected end of input", (char)0); - - return str[i++]; - } - - /* encode_utf8(pt, out) - * - * Encode pt as UTF-8 and add it to out. - */ - void encode_utf8(long pt, string & out) { - if (pt < 0) - return; - - if (pt < 0x80) { - out += static_cast(pt); - } else if (pt < 0x800) { - out += static_cast((pt >> 6) | 0xC0); - out += static_cast((pt & 0x3F) | 0x80); - } else if (pt < 0x10000) { - out += static_cast((pt >> 12) | 0xE0); - out += static_cast(((pt >> 6) & 0x3F) | 0x80); - out += static_cast((pt & 0x3F) | 0x80); - } else { - out += static_cast((pt >> 18) | 0xF0); - out += static_cast(((pt >> 12) & 0x3F) | 0x80); - out += static_cast(((pt >> 6) & 0x3F) | 0x80); - out += static_cast((pt & 0x3F) | 0x80); - } - } - - /* parse_string() - * - * Parse a string, starting at the current position. - */ - string parse_string() { - string out; - long last_escaped_codepoint = -1; - while (true) { - if (i == str.size()) - return fail("unexpected end of input in string", ""); - - char ch = str[i++]; - - if (ch == '"') { - encode_utf8(last_escaped_codepoint, out); - return out; - } - - if (in_range(ch, 0, 0x1f)) - return fail("unescaped " + esc(ch) + " in string", ""); - - // The usual case: non-escaped characters - if (ch != '\\') { - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = -1; - out += ch; - continue; - } - - // Handle escapes - if (i == str.size()) - return fail("unexpected end of input in string", ""); - - ch = str[i++]; - - if (ch == 'u') { - // Extract 4-byte escape sequence - string esc = str.substr(i, 4); - // Explicitly check length of the substring. The following loop - // relies on std::string returning the terminating NUL when - // accessing str[length]. Checking here reduces brittleness. - if (esc.length() < 4) { - return fail("bad \\u escape: " + esc, ""); - } - for (size_t j = 0; j < 4; j++) { - if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') - && !in_range(esc[j], '0', '9')) - return fail("bad \\u escape: " + esc, ""); - } - - long codepoint = strtol(esc.data(), nullptr, 16); - - // JSON specifies that characters outside the BMP shall be encoded as a pair - // of 4-hex-digit \u escapes encoding their surrogate pair components. Check - // whether we're in the middle of such a beast: the previous codepoint was an - // escaped lead (high) surrogate, and this is a trail (low) surrogate. - if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) - && in_range(codepoint, 0xDC00, 0xDFFF)) { - // Reassemble the two surrogate pairs into one astral-plane character, per - // the UTF-16 algorithm. - encode_utf8((((last_escaped_codepoint - 0xD800) << 10) - | (codepoint - 0xDC00)) + 0x10000, out); - last_escaped_codepoint = -1; - } else { - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = codepoint; - } - - i += 4; - continue; - } - - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = -1; - - if (ch == 'b') { - out += '\b'; - } else if (ch == 'f') { - out += '\f'; - } else if (ch == 'n') { - out += '\n'; - } else if (ch == 'r') { - out += '\r'; - } else if (ch == 't') { - out += '\t'; - } else if (ch == '"' || ch == '\\' || ch == '/') { - out += ch; - } else { - return fail("invalid escape character " + esc(ch), ""); - } - } - } - - /* parse_number() - * - * Parse a double. - */ - Json parse_number() { - size_t start_pos = i; - - if (str[i] == '-') - i++; - - // Integer part - if (str[i] == '0') { - i++; - if (in_range(str[i], '0', '9')) - return fail("leading 0s not permitted in numbers"); - } else if (in_range(str[i], '1', '9')) { - i++; - while (in_range(str[i], '0', '9')) - i++; - } else { - return fail("invalid " + esc(str[i]) + " in number"); - } - - if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' - && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { - return std::atoi(str.c_str() + start_pos); - } - - // Decimal part - if (str[i] == '.') { - i++; - if (!in_range(str[i], '0', '9')) - return fail("at least one digit required in fractional part"); - - while (in_range(str[i], '0', '9')) - i++; - } - - // Exponent part - if (str[i] == 'e' || str[i] == 'E') { - i++; - - if (str[i] == '+' || str[i] == '-') - i++; - - if (!in_range(str[i], '0', '9')) - return fail("at least one digit required in exponent"); - - while (in_range(str[i], '0', '9')) - i++; - } - - return std::strtod(str.c_str() + start_pos, nullptr); - } - - /* expect(str, res) - * - * Expect that 'str' starts at the character that was just read. If it does, advance - * the input and return res. If not, flag an error. - */ - Json expect(const string &expected, Json res) { - assert(i != 0); - i--; - if (str.compare(i, expected.length(), expected) == 0) { - i += expected.length(); - return res; - } else { - return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); - } - } - - /* parse_json() - * - * Parse a JSON object. - */ - Json parse_json(int depth) { - if (depth > max_depth) { - return fail("exceeded maximum nesting depth"); - } - - char ch = get_next_token(); - if (failed) - return Json(); - - if (ch == '-' || (ch >= '0' && ch <= '9')) { - i--; - return parse_number(); - } - - if (ch == 't') - return expect("true", true); - - if (ch == 'f') - return expect("false", false); - - if (ch == 'n') - return expect("null", Json()); - - if (ch == '"') - return parse_string(); - - if (ch == '{') { - map data; - ch = get_next_token(); - if (ch == '}') - return data; - - while (1) { - if (ch != '"') - return fail("expected '\"' in object, got " + esc(ch)); - - string key = parse_string(); - if (failed) - return Json(); - - ch = get_next_token(); - if (ch != ':') - return fail("expected ':' in object, got " + esc(ch)); - - data[std::move(key)] = parse_json(depth + 1); - if (failed) - return Json(); - - ch = get_next_token(); - if (ch == '}') - break; - if (ch != ',') - return fail("expected ',' in object, got " + esc(ch)); - - ch = get_next_token(); - } - return data; - } - - if (ch == '[') { - vector data; - ch = get_next_token(); - if (ch == ']') - return data; - - while (1) { - i--; - data.push_back(parse_json(depth + 1)); - if (failed) - return Json(); - - ch = get_next_token(); - if (ch == ']') - break; - if (ch != ',') - return fail("expected ',' in list, got " + esc(ch)); - - ch = get_next_token(); - (void)ch; - } - return data; - } - - return fail("expected value, got " + esc(ch)); - } -}; -}//namespace { - -Json Json::parse(const string &in, string &err, JsonParse strategy) { - JsonParser parser { in, 0, err, false, strategy }; - Json result = parser.parse_json(0); - - // Check for any trailing garbage - parser.consume_garbage(); - if (parser.failed) - return Json(); - if (parser.i != in.size()) - return parser.fail("unexpected trailing " + esc(in[parser.i])); - - return result; -} - -// Documented in json11.hpp -vector Json::parse_multi(const string &in, - std::string::size_type &parser_stop_pos, - string &err, - JsonParse strategy) { - JsonParser parser { in, 0, err, false, strategy }; - parser_stop_pos = 0; - vector json_vec; - while (parser.i != in.size() && !parser.failed) { - json_vec.push_back(parser.parse_json(0)); - if (parser.failed) - break; - - // Check for another object - parser.consume_garbage(); - if (parser.failed) - break; - parser_stop_pos = parser.i; - } - return json_vec; -} - -/* * * * * * * * * * * * * * * * * * * * - * Shape-checking - */ - -bool Json::has_shape(const shape & types, string & err) const { - if (!is_object()) { - err = "expected JSON object, got " + dump(); - return false; - } - - for (auto & item : types) { - if ((*this)[item.first].type() != item.second) { - err = "bad type for " + item.first + " in " + dump(); - return false; - } - } - - return true; -} - -} // namespace json11 diff --git a/source/lib/json11/json11.hpp b/source/lib/json11/json11.hpp deleted file mode 100644 index 0c47d050..00000000 --- a/source/lib/json11/json11.hpp +++ /dev/null @@ -1,232 +0,0 @@ -/* json11 - * - * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. - * - * The core object provided by the library is json11::Json. A Json object represents any JSON - * value: null, bool, number (int or double), string (std::string), array (std::vector), or - * object (std::map). - * - * Json objects act like values: they can be assigned, copied, moved, compared for equality or - * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and - * Json::parse (static) to parse a std::string as a Json object. - * - * Internally, the various types of Json object are represented by the JsonValue class - * hierarchy. - * - * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, - * so some JSON implementations distinguish between integers and floating-point numbers, while - * some don't. In json11, we choose the latter. Because some JSON implementations (namely - * Javascript itself) treat all numbers as the same type, distinguishing the two leads - * to JSON that will be *silently* changed by a round-trip through those implementations. - * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also - * provides integer helpers. - * - * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the - * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 - * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch - * will be exact for +/- 275 years.) - */ - -/* Copyright (c) 2013 Dropbox, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - #if _MSC_VER <= 1800 // VS 2013 - #ifndef noexcept - #define noexcept throw() - #endif - - #ifndef snprintf - #define snprintf _snprintf_s - #endif - #endif -#endif - -namespace json11 { - -enum JsonParse { - STANDARD, COMMENTS -}; - -class JsonValue; - -class Json final { -public: - // Types - enum Type { - NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT - }; - - // Array and object typedefs - typedef std::vector array; - typedef std::map object; - - // Constructors for the various types of JSON value. - Json() noexcept; // NUL - Json(std::nullptr_t) noexcept; // NUL - Json(double value); // NUMBER - Json(int value); // NUMBER - Json(bool value); // BOOL - Json(const std::string &value); // STRING - Json(std::string &&value); // STRING - Json(const char * value); // STRING - Json(const array &values); // ARRAY - Json(array &&values); // ARRAY - Json(const object &values); // OBJECT - Json(object &&values); // OBJECT - - // Implicit constructor: anything with a to_json() function. - template - Json(const T & t) : Json(t.to_json()) {} - - // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) - template ().begin()->first)>::value - && std::is_constructible().begin()->second)>::value, - int>::type = 0> - Json(const M & m) : Json(object(m.begin(), m.end())) {} - - // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) - template ().begin())>::value, - int>::type = 0> - Json(const V & v) : Json(array(v.begin(), v.end())) {} - - // This prevents Json(some_pointer) from accidentally producing a bool. Use - // Json(bool(some_pointer)) if that behavior is desired. - Json(void *) = delete; - - // Accessors - Type type() const; - - bool is_null() const { return type() == NUL; } - bool is_number() const { return type() == NUMBER; } - bool is_bool() const { return type() == BOOL; } - bool is_string() const { return type() == STRING; } - bool is_array() const { return type() == ARRAY; } - bool is_object() const { return type() == OBJECT; } - - // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not - // distinguish between integer and non-integer numbers - number_value() and int_value() - // can both be applied to a NUMBER-typed object. - double number_value() const; - int int_value() const; - - // Return the enclosed value if this is a boolean, false otherwise. - bool bool_value() const; - // Return the enclosed string if this is a string, "" otherwise. - const std::string &string_value() const; - // Return the enclosed std::vector if this is an array, or an empty vector otherwise. - const array &array_items() const; - // Return the enclosed std::map if this is an object, or an empty map otherwise. - const object &object_items() const; - - // Return a reference to arr[i] if this is an array, Json() otherwise. - const Json & operator[](size_t i) const; - // Return a reference to obj[key] if this is an object, Json() otherwise. - const Json & operator[](const std::string &key) const; - - // Serialize. - void dump(std::string &out) const; - std::string dump() const { - std::string out; - dump(out); - return out; - } - - // Parse. If parse fails, return Json() and assign an error message to err. - static Json parse(const std::string & in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD); - static Json parse(const char * in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD) { - if (in) { - return parse(std::string(in), err, strategy); - } else { - err = "null input"; - return nullptr; - } - } - // Parse multiple objects, concatenated or separated by whitespace - static std::vector parse_multi( - const std::string & in, - std::string::size_type & parser_stop_pos, - std::string & err, - JsonParse strategy = JsonParse::STANDARD); - - static inline std::vector parse_multi( - const std::string & in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD) { - std::string::size_type parser_stop_pos; - return parse_multi(in, parser_stop_pos, err, strategy); - } - - bool operator== (const Json &rhs) const; - bool operator< (const Json &rhs) const; - bool operator!= (const Json &rhs) const { return !(*this == rhs); } - bool operator<= (const Json &rhs) const { return !(rhs < *this); } - bool operator> (const Json &rhs) const { return (rhs < *this); } - bool operator>= (const Json &rhs) const { return !(*this < rhs); } - - /* has_shape(types, err) - * - * Return true if this is a JSON object and, for each item in types, has a field of - * the given type. If not, return false and set err to a descriptive message. - */ - typedef std::initializer_list> shape; - bool has_shape(const shape & types, std::string & err) const; - -private: - std::shared_ptr m_ptr; -}; - -// Internal class hierarchy - JsonValue objects are not exposed to users of this API. -class JsonValue { -protected: - friend class Json; - friend class JsonInt; - friend class JsonDouble; - virtual Json::Type type() const = 0; - virtual bool equals(const JsonValue * other) const = 0; - virtual bool less(const JsonValue * other) const = 0; - virtual void dump(std::string &out) const = 0; - virtual double number_value() const; - virtual int int_value() const; - virtual bool bool_value() const; - virtual const std::string &string_value() const; - virtual const Json::array &array_items() const; - virtual const Json &operator[](size_t i) const; - virtual const Json::object &object_items() const; - virtual const Json &operator[](const std::string &key) const; - virtual ~JsonValue() {} -}; - -} // namespace json11 diff --git a/source/lib/pybind11/attr.h b/source/lib/pybind11/attr.h deleted file mode 100644 index 2b0c9046..00000000 --- a/source/lib/pybind11/attr.h +++ /dev/null @@ -1,683 +0,0 @@ -/* - pybind11/attr.h: Infrastructure for processing custom - type and function attributes - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "cast.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// \addtogroup annotations -/// @{ - -/// Annotation for methods -struct is_method -{ - handle class_; - is_method(const handle &c) - : class_(c) {} -}; - -/// Annotation for operators -struct is_operator -{ -}; - -/// Annotation for classes that cannot be subclassed -struct is_final -{ -}; - -/// Annotation for parent scope -struct scope -{ - handle value; - scope(const handle &s) - : value(s) {} -}; - -/// Annotation for documentation -struct doc -{ - const char *value; - doc(const char *value) - : value(value) {} -}; - -/// Annotation for function names -struct name -{ - const char *value; - name(const char *value) - : value(value) {} -}; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling -{ - handle value; - sibling(const handle &value) - : value(value.ptr()) {} -}; - -/// Annotation indicating that a class derives from another given type -template -struct base -{ - - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute -}; - -/// Keep patient alive while nurse lives -template -struct keep_alive -{ -}; - -/// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance -{ -}; - -/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr -{ -}; - -/// Annotation which enables the buffer protocol for a type -struct buffer_protocol -{ -}; - -/// Annotation which requests that a special metaclass is created for a type -struct metaclass -{ - handle value; - - PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute - - /// Override pybind11's default metaclass - explicit metaclass(handle value) - : value(value) {} -}; - -/// Annotation that marks a class as local to the module: -struct module_local -{ - const bool value; - constexpr module_local(bool v = true) - : value(v) {} -}; - -/// Annotation to mark enums as an arithmetic type -struct arithmetic -{ -}; - -/// Mark a function for addition at the beginning of the existing overload chain instead of the end -struct prepend -{ -}; - -/** \rst - A call policy which places one or more guard variables (``Ts...``) around the function call. - - For example, this definition: - - .. code-block:: cpp - - m.def("foo", foo, py::call_guard()); - - is equivalent to the following pseudocode: - - .. code-block:: cpp - - m.def("foo", [](args...) { - T scope_guard; - return foo(args...); // forwarded arguments - }); - \endrst */ -template -struct call_guard; - -template <> -struct call_guard<> -{ - using type = detail::void_type; -}; - -template -struct call_guard -{ - static_assert(std::is_default_constructible::value, - "The guard type must be default constructible"); - - using type = T; -}; - -template -struct call_guard -{ - struct type - { - T guard {}; // Compose multiple guard types with left-to-right default-constructor order - typename call_guard::type next {}; - }; -}; - -/// @} annotations - -PYBIND11_NAMESPACE_BEGIN(detail) -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template -struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); - -/// Internal data structure which holds metadata about a keyword argument -struct argument_record -{ - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - handle value; ///< Associated Python object - bool convert : 1; ///< True if the argument is allowed to convert when loading - bool none : 1; ///< True if None is allowed when loading - - argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) {} -}; - -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) -struct function_record -{ - function_record() - : is_constructor(false), is_new_style_constructor(false), is_stateless(false), is_operator(false), is_method(false), has_args(false), has_kwargs(false), has_kw_only_args(false), prepend(false) {} - - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl)(function_call &) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = {}; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data)(function_record *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor : 1; - - /// True if this is a new-style `__init__` defined in `detail/init.h` - bool is_new_style_constructor : 1; - - /// True if this is a stateless function pointer - bool is_stateless : 1; - - /// True if this is an operator (__add__), etc. - bool is_operator : 1; - - /// True if this is a method - bool is_method : 1; - - /// True if the function has a '*args' argument - bool has_args : 1; - - /// True if the function has a '**kwargs' argument - bool has_kwargs : 1; - - /// True once a 'py::kw_only' is encountered (any following args are keyword-only) - bool has_kw_only_args : 1; - - /// True if this function is to be inserted at the beginning of the overload resolution chain - bool prepend : 1; - - /// Number of arguments (including py::args and/or py::kwargs, if present) - std::uint16_t nargs; - - /// Number of trailing arguments (counted in `nargs`) that are keyword-only - std::uint16_t nargs_kw_only = 0; - - /// Number of leading arguments (counted in `nargs`) that are positional-only - std::uint16_t nargs_pos_only = 0; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Python handle to the parent scope (a class or a module) - handle scope; - - /// Python handle to the sibling function representing an overload chain - handle sibling; - - /// Pointer to next overload - function_record *next = nullptr; -}; - -/// Special data structure which (temporarily) holds metadata about a bound class -struct type_record -{ - PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), default_holder(true), module_local(false), is_final(false) {} - - /// Handle to the parent scope - handle scope; - - /// Name of the class - const char *name = nullptr; - - // Pointer to RTTI type_info data structure - const std::type_info *type = nullptr; - - /// How large is the underlying C++ type? - size_t type_size = 0; - - /// What is the alignment of the underlying C++ type? - size_t type_align = 0; - - /// How large is the type's holder? - size_t holder_size = 0; - - /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = nullptr; - - /// Function pointer to class_<..>::init_instance - void (*init_instance)(instance *, const void *) = nullptr; - - /// Function pointer to class_<..>::dealloc - void (*dealloc)(detail::value_and_holder &) = nullptr; - - /// List of base classes of the newly created type - list bases; - - /// Optional docstring - const char *doc = nullptr; - - /// Custom metaclass (optional) - handle metaclass; - - /// Multiple inheritance marker - bool multiple_inheritance : 1; - - /// Does the class manage a __dict__? - bool dynamic_attr : 1; - - /// Does the class implement the buffer protocol? - bool buffer_protocol : 1; - - /// Is the default (unique_ptr) holder type used? - bool default_holder : 1; - - /// Is the class definition local to the module shared object? - bool module_local : 1; - - /// 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 + "\""); - } - - if (default_holder != base_info->default_holder) - { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + (default_holder ? "does not have" : "has") + " a non-default holder type while its base \"" + tname + "\" " + (base_info->default_holder ? "does not" : "does")); - } - - bases.append((PyObject *) base_info->type); - - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; - - if (caster) - base_info->implicit_casts.emplace_back(type, caster); - } -}; - -inline function_call::function_call(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 -{ -}; - -/** - * Partial template specializations to process custom attributes provided to - * cpp_function_ and class_. These are either used to initialize the respective - * fields in the type_record and function_record data structures or executed at - * runtime to deal with custom call policies (e.g. keep_alive). - */ -template -struct process_attribute; - -template -struct process_attribute_default -{ - /// Default implementation: do nothing - static void init(const T &, function_record *) {} - static void init(const T &, type_record *) {} - static void precall(function_call &) {} - static void postcall(function_call &, handle) {} -}; - -/// Process an attribute specifying the function's name -template <> -struct process_attribute : process_attribute_default -{ - static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring -template <> -struct process_attribute : process_attribute_default -{ - static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> -struct process_attribute : process_attribute_default -{ - static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } -}; -template <> -struct process_attribute : process_attribute -{ -}; - -/// Process an attribute indicating the function's return value policy -template <> -struct process_attribute : process_attribute_default -{ - static void init(const return_value_policy &p, function_record *r) { r->policy = p; } -}; - -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> -struct process_attribute : process_attribute_default -{ - static void init(const sibling &s, function_record *r) { r->sibling = s.value; } -}; - -/// Process an attribute which indicates that this function is a method -template <> -struct process_attribute : process_attribute_default -{ - static void init(const is_method &s, function_record *r) - { - r->is_method = true; - r->scope = s.class_; - } -}; - -/// Process an attribute which indicates the parent scope of a method -template <> -struct process_attribute : process_attribute_default -{ - static void init(const scope &s, function_record *r) { r->scope = s.value; } -}; - -/// Process an attribute which indicates that this function is an operator -template <> -struct process_attribute : process_attribute_default -{ - static void init(const is_operator &, function_record *r) { r->is_operator = true; } -}; - -template <> -struct process_attribute : process_attribute_default -{ - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } -}; - -inline void process_kw_only_arg(const arg &a, function_record *r) -{ - if (!a.name || strlen(a.name) == 0) - pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); - ++r->nargs_kw_only; -} - -/// Process a keyword argument attribute (*without* a default value) -template <> -struct process_attribute : process_attribute_default -{ - static void init(const arg &a, function_record *r) - { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - - if (r->has_kw_only_args) - process_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("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); - - if (!a.value) - { -#if !defined(NDEBUG) - std::string descr("'"); - if (a.name) - descr += std::string(a.name) + ": "; - descr += a.type + "'"; - if (r->is_method) - { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else - descr += " in method of '" + (std::string) str(r->scope) + "'"; - } - else if (r->name) - { - descr += " in function '" + (std::string) r->name + "'"; - } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); -#else - pybind11_fail("arg(): could not convert default argument " - "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); -#endif - } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - - if (r->has_kw_only_args) - process_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) - { - r->has_kw_only_args = true; - } -}; - -/// Process a positional-only-argument maker -template <> -struct process_attribute : process_attribute_default -{ - static void init(const pos_only &, function_record *r) - { - r->nargs_pos_only = static_cast(r->args.size()); - } -}; - -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) -template -struct process_attribute::value>> : process_attribute_default -{ - static void init(const handle &h, type_record *r) { r->bases.append(h); } -}; - -/// Process a parent class attribute (deprecated, does not support multiple inheritance) -template -struct process_attribute> : process_attribute_default> -{ - static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } -}; - -/// Process a multiple inheritance attribute -template <> -struct process_attribute : process_attribute_default -{ - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } -}; - -template <> -struct process_attribute : process_attribute_default -{ - static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } -}; - -template <> -struct process_attribute : process_attribute_default -{ - static void init(const 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; } -}; - -template <> -struct process_attribute : process_attribute_default -{ - static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } -}; - -template <> -struct process_attribute : process_attribute_default -{ - static void init(const module_local &l, type_record *r) { r->module_local = l.value; } -}; - -/// Process 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; } -}; - -/// Process an 'arithmetic' attribute for enums (does nothing here) -template <> -struct process_attribute : process_attribute_default -{ -}; - -template -struct process_attribute> : process_attribute_default> -{ -}; - -/** - * Process a keep_alive call policy -- invokes keep_alive_impl during the - * pre-call handler if both Nurse, Patient != 0 and use the post-call handler - * otherwise - */ -template -struct process_attribute> : public process_attribute_default> -{ - template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } - template = 0> - static void postcall(function_call &, handle) {} - template = 0> - static void precall(function_call &) {} - template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } -}; - -/// Recursively iterate over variadic template arguments -template -struct process_attributes -{ - static void init(const Args &... args, function_record *r) - { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0)... }; - ignore_unused(unused); - } - static void init(const Args &... args, type_record *r) - { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0)... }; - ignore_unused(unused); - } - static void precall(function_call &call) - { - int unused[] = { 0, (process_attribute::type>::precall(call), 0)... }; - ignore_unused(unused); - } - static void postcall(function_call &call, handle fn_ret) - { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0)... }; - ignore_unused(unused); - } -}; - -template -using is_call_guard = is_instantiation; - -/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) -template -using extract_guard_t = typename exactly_one_t, Extra...>::type; - -/// Check the number of named arguments at compile time -template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> -constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) -{ - return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; -} - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/source/lib/pybind11/buffer_info.h b/source/lib/pybind11/buffer_info.h deleted file mode 100644 index f8e0d7ea..00000000 --- a/source/lib/pybind11/buffer_info.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - pybind11/buffer_info.h: Python buffer object interface - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -PYBIND11_NAMESPACE_BEGIN(detail) - -// Default, C-style strides -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) - strides[i - 1] = strides[i] * shape[i]; - return strides; -} - -// 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) -{ - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) - strides[i] = strides[i - 1] * shape[i - 1]; - return strides; -} - -PYBIND11_NAMESPACE_END(detail) - -/// Information record describing a Python buffer object -struct buffer_info -{ - void *ptr = nullptr; // Pointer to the underlying storage - ssize_t itemsize = 0; // Size of individual items in bytes - ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() - ssize_t ndim = 0; // Number of dimensions - std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of bytes between adjacent entries (for each per dimension) - bool readonly = false; // flag to indicate if the underlying storage may be written to - - 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"); - 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) {} - - template - 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) - { - this->m_view = view; - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info &operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) - { - (*this) = std::move(other); - } - - buffer_info &operator=(buffer_info &&rhs) - { - ptr = rhs.ptr; - itemsize = rhs.itemsize; - size = rhs.size; - format = std::move(rhs.format); - ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(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); - delete m_view; - } - } - - Py_buffer *view() const { return m_view; } - Py_buffer *&view() { return m_view; } - -private: - struct private_ctr_tag - { - }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in, 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) - -template -struct compare_buffer_info -{ - static bool compare(const buffer_info &b) - { - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); - } -}; - -template -struct compare_buffer_info::value>> -{ - static bool compare(const buffer_info &b) - { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); - } -}; - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/source/lib/pybind11/cast.h b/source/lib/pybind11/cast.h deleted file mode 100644 index b0d1cae4..00000000 --- a/source/lib/pybind11/cast.h +++ /dev/null @@ -1,2729 +0,0 @@ -/* - pybind11/cast.h: Partial template specializations to cast between - C++ and Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/descr.h" -#include "detail/internals.h" -#include "detail/typeid.h" -#include "pytypes.h" -#include -#include -#include -#include - -#if defined(PYBIND11_CPP17) -#if defined(__has_include) -#if __has_include() -#define PYBIND11_HAS_STRING_VIEW -#endif -#elif defined(_MSC_VER) -#define PYBIND11_HAS_STRING_VIEW -#endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L -#define PYBIND11_HAS_U8STRING -#endif - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support -{ -public: - /// A new patient frame is created when a function is entered - loader_life_support() - { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() - { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && !stack.empty() && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) - { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) - { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } - else - { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) -{ - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) - { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) - continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) - { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) - { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) - { - if (known == tinfo) - { - found = true; - break; - } - } - if (!found) - bases.push_back(tinfo); - } - } - else if (type->tp_bases) - { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) - { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) -{ - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info *get_type_info(PyTypeObject *type) -{ - auto &bases = all_type_info(type); - if (bases.empty()) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) -{ - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) -{ - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) -{ - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) - { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) -{ - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -// Searches the inheritance graph for a registered Python instance, using all_type_info(). -PYBIND11_NOINLINE inline handle find_registered_python_instance(void *src, - const detail::type_info *tinfo) -{ - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) - { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) - { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - return handle(); -} - -struct value_and_holder -{ - instance *inst = nullptr; - size_t index = 0u; - const detail::type_info *type = nullptr; - void **vh = nullptr; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) - : inst { i }, index { index }, type { type }, vh { inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos] } - { - } - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() = default; - - // Used for past-the-end iterator - value_and_holder(size_t index) - : index { index } {} - - template - V *&value_ptr() const - { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template - H &holder() const - { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const - { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) - { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const - { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) - { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders -{ -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) - : inst { inst }, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator - { - private: - instance *inst = nullptr; - const type_vec *types = nullptr; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst { inst }, types { tinfo }, curr(inst /* instance */, types->empty() ? nullptr : (*types)[0] /* type info */, 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - { - } - // Past-the-end iterator: - iterator(size_t end) - : curr(end) {} - - public: - bool operator==(const iterator &other) const { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } - iterator &operator++() - { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) - { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) - ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) -{ - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" + get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() -{ - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) - { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else - { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) - { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) - throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) - throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() -{ - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) -{ - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() -{ - if (!PyErr_Occurred()) - { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) - { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) - { - auto *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) - { - int lineno = PyFrame_GetLineNumber(frame); - errorString += " " + handle(frame->f_code->co_filename).cast() + "(" + std::to_string(lineno) + "): " + handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type) -{ - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) - { - for (const auto &vh : values_and_holders(it->second)) - { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() -{ -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState *) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState *) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); - -class type_caster_generic -{ -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) {} - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {} - - bool load(handle src, bool convert) - { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) - { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - if (handle registered_inst = find_registered_python_instance(src, tinfo)) - return registered_inst; - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) - { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else - { -#if defined(NDEBUG) - throw cast_error("return_value_policy = copy, but type is " - "non-copyable! (compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = copy, but type " + type_name + " is non-copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else - { -#if defined(NDEBUG) - throw cast_error("return_value_policy = move, but type is neither " - "movable nor copyable! " - "(compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = move, but type " + type_name + " is neither movable nor copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) - { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) - { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) - { - vptr = type->operator_new(type->type_size); - } - else - { -#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - std::align_val_t(type->type_align)); - else -#endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) - { - for (auto &cast : typeinfo->implicit_casts) - { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) - { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) - { - for (auto &converter : *typeinfo->direct_conversions) - { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) - { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) - { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = type::handle_of(src); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) - { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) - { - if (!src) - return false; - if (!typeinfo) - return try_load_foreign_module_local(src); - if (src.is_none()) - { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) - return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) - { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) - { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) - { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) - { - for (auto base : bases) - { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) - { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) - { - for (auto &converter : typeinfo->implicit_conversions) - { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) - { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) - { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) - { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) - { - if (auto *tpi = get_type_info(cast_type)) - return { src, const_cast(tpi) }; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return { nullptr, nullptr }; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template -struct is_copy_constructible : std::is_copy_constructible -{ -}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template -struct is_copy_constructible, std::is_same, - // Avoid infinite recursion - negation>>::value>> : is_copy_constructible -{ -}; - -// Likewise for std::pair -// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves -// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). -template -struct is_copy_constructible> - : all_of, is_copy_constructible> -{ -}; - -// The same problems arise with std::is_copy_assignable, so we use the same workaround. -template -struct is_copy_assignable : std::is_copy_assignable -{ -}; -template -struct is_copy_assignable, std::is_same>::value>> : is_copy_assignable -{ -}; -template -struct is_copy_assignable> - : all_of, is_copy_assignable> -{ -}; - -PYBIND11_NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -// -// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with -// std::enable_if. User provided specializations will always have higher priority than -// the default implementation and specialization provided in polymorphic_type_hook_base. -template -struct polymorphic_type_hook_base -{ - static const void *get(const itype *src, const std::type_info *&) { return src; } -}; -template -struct polymorphic_type_hook_base::value>> -{ - static const void *get(const itype *src, const std::type_info *&type) - { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; -template -struct polymorphic_type_hook : public polymorphic_type_hook_base -{ -}; - -PYBIND11_NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template -class type_caster_base : public type_caster_generic -{ - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() - : type_caster_base(typeid(type)) {} - explicit type_caster_base(const std::type_info &info) - : type_caster_generic(info) {} - - static handle cast(const itype &src, return_value_policy policy, handle parent) - { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) - { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) - { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) - { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return { vsrc, tpi }; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) - { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) - { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template - using cast_op_type = detail::cast_op_type; - - operator itype *() { return (type *) value; } - operator itype &() - { - if (!value) - throw reference_cast_error(); - return *((itype *) value); - } - -protected: - using Constructor = void *(*) (const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor {}) - { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor {}) - { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template -class type_caster : public type_caster_base -{ -}; -template -using make_caster = type_caster>; - -// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template -typename make_caster::template cast_op_type cast_op(make_caster &caster) -{ - return caster.operator typename make_caster::template cast_op_type(); -} -template -typename make_caster::template cast_op_type::type> -cast_op(make_caster &&caster) -{ - return std::move(caster).operator typename make_caster::template cast_op_type::type>(); -} - -template -class type_caster> -{ -private: - using caster_t = make_caster; - caster_t subcaster; - using 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 || 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); } - static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) - { - // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) - policy = return_value_policy::automatic_reference; - return caster_t::cast(&src.get(), policy, parent); - } - template - using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return 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 handle cast(T_ *src, return_value_policy policy, handle parent) \ - { \ - if (!src) \ - return none().release(); \ - if (policy == return_value_policy::take_ownership) \ - { \ - auto h = cast(std::move(*src), policy, parent); \ - delete src; \ - return h; \ - } \ - else \ - { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type *() { return &value; } \ - operator type &() { return value; } \ - operator type &&() && { return std::move(value); } \ - template \ - using cast_op_type = pybind11::detail::movable_cast_op_type - -template -using is_std_char_type = any_of< - std::is_same, /* std::string */ -#if defined(PYBIND11_HAS_U8STRING) - std::is_same, /* std::u8string */ -#endif - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ - >; - -template -struct type_caster::value && !is_std_char_type::value>> -{ - using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; - using py_type = conditional_t::value, double, _py_type_1>; - -public: - bool load(handle src, bool convert) - { - py_type py_value; - - if (!src) - return false; - -#if !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__"); }; -#endif - - if (std::is_floating_point::value) - { - if (convert || PyFloat_Check(src.ptr())) - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else - return false; - } - else if (PyFloat_Check(src.ptr())) - { - return false; - } - else if (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr())) - { - return false; - } - else - { - handle src_or_index = src; -#if PY_VERSION_HEX < 0x03080000 - object 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) - return false; - } - 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()); - } - } - - // Python API reported an error - bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - - // 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)) - { - PyErr_Clear(); - if (py_err && convert && PyNumber_Check(src.ptr())) - { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return false; - } - - value = (T) py_value; - return true; - } - - template - static typename std::enable_if::value, handle>::type - 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)), handle>::type - 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)), handle>::type - 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)), handle>::type - 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)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) - { - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } - - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); -}; - -template -struct void_caster -{ -public: - bool load(handle src, bool) - { - if (src && src.is_none()) - return true; - return false; - } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) - { - return none().inc_ref(); - } - PYBIND11_TYPE_CASTER(T, _("None")); -}; - -template <> -class type_caster : public void_caster -{ -}; - -template <> -class type_caster : public type_caster -{ -public: - using type_caster::cast; - - bool load(handle h, bool) - { - if (!h) - { - return false; - } - else if (h.is_none()) - { - value = nullptr; - return true; - } - - /* Check if this is a capsule */ - if (isinstance(h)) - { - value = reinterpret_borrow(h); - return true; - } - - /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) 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; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) - { - if (ptr) - return capsule(ptr).release(); - else - return none().inc_ref(); - } - - template - using cast_op_type = void *&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); - -private: - void *value = nullptr; -}; - -template <> -class type_caster : public void_caster -{ -}; - -template <> -class type_caster -{ -public: - bool load(handle src, bool convert) - { - if (!src) - return false; - else if (src.ptr() == Py_True) - { - value = true; - return true; - } - else if (src.ptr() == Py_False) - { - value = false; - return true; - } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) - { - // (allow non-implicit conversion for numpy booleans) - - Py_ssize_t res = -1; - if (src.is_none()) - { - res = 0; // None is implicitly converted to False - } -#if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) - { - res = PyObject_IsTrue(src.ptr()); - } -#else - // Alternate approach for CPython: this does the same as the above, but optimized - // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) - { - if (PYBIND11_NB_BOOL(tp_as_number)) - { - res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); - } - } -#endif - if (res == 0 || res == 1) - { - value = (bool) res; - return true; - } - else - { - PyErr_Clear(); - } - } - return false; - } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) - { - return handle(src ? Py_True : Py_False).inc_ref(); - } - PYBIND11_TYPE_CASTER(bool, _("bool")); -}; - -// Helper class for UTF-{8,16,32} C++ stl strings: -template -struct string_caster -{ - using CharT = typename StringType::value_type; - - // Simplify life by being able to assume standard char sizes (the standard only guarantees - // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); -#if defined(PYBIND11_HAS_U8STRING) - 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"); - // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) - static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); - static constexpr size_t UTF_N = 8 * sizeof(CharT); - - bool load(handle src, bool) - { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif - handle load_src = src; - if (!src) - { - return false; - } - else if (!PyUnicode_Check(load_src.ptr())) - { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (std::is_same::value) - { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) - { - PyErr_Clear(); - return false; - } - load_src = temp; -#endif - } - - 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); - if (UTF_N > 8) - { - buffer++; - length--; - } // Skip BOM for UTF-16/32 - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) - loader_life_support::add_patient(utfNbytes); - - return true; - } - - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) - { - const char *buffer = reinterpret_cast(src.data()); - 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, _(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) - { -#if !defined(PYPY_VERSION) - return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#else - // PyPy 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); -#endif - } - - // When loading into a std::string or char*, accept a bytes object as-is (i.e. - // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. - // which supports loading a unicode from a str, doesn't take this path. - template - bool load_bytes(enable_if_t::value, handle> src) - { - if (PYBIND11_BYTES_CHECK(src.ptr())) - { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) - { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - } - - return false; - } - - template - bool load_bytes(enable_if_t::value, handle>) { return false; } -}; - -template -struct type_caster, enable_if_t::value>> - : string_caster> -{ -}; - -#ifdef PYBIND11_HAS_STRING_VIEW -template -struct type_caster, enable_if_t::value>> - : string_caster, true> -{ -}; -#endif - -// Type caster for C-style strings. We basically use a std::string type caster, but also add the -// ability to use None as a nullptr char* (which the string caster doesn't allow). -template -struct type_caster::value>> -{ - using StringType = std::basic_string; - using StringCaster = type_caster; - StringCaster str_caster; - bool none = false; - CharT one_char = 0; - -public: - bool load(handle src, bool convert) - { - if (!src) - return false; - if (src.is_none()) - { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) - return false; - none = true; - return true; - } - return str_caster.load(src, convert); - } - - static handle cast(const CharT *src, return_value_policy policy, handle parent) - { - if (src == nullptr) - return pybind11::none().inc_ref(); - return StringCaster::cast(StringType(src), policy, parent); - } - - static handle cast(CharT src, return_value_policy policy, handle parent) - { - if (std::is_same::value) - { - handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) - throw error_already_set(); - return s; - } - return StringCaster::cast(StringType(1, src), policy, parent); - } - - operator CharT *() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT &() - { - if (none) - throw value_error("Cannot convert None to a character"); - - auto &value = static_cast(str_caster); - size_t str_len = value.size(); - if (str_len == 0) - throw value_error("Cannot convert empty string to a character"); - - // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) - { - auto v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence - - if (char0_bytes == str_len) - { - // If we have a 128-255 value, we can decode it into a single char: - if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) - { // 0x110000xx 0x10xxxxxx - 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)"); - } - } - - // 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)"); - } - - 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 = _(PYBIND11_STRING_NAME); - template - using cast_op_type = pybind11::detail::cast_op_type<_T>; -}; - -// Base implementation for std::tuple and std::pair -template