From 3ac929b48b820aabc54b6ddf889e7e5f88615ddf Mon Sep 17 00:00:00 2001 From: Shintaro Iwasaki Date: Fri, 23 Sep 2022 15:54:07 -0700 Subject: [PATCH] [BUILD] Download pybind11 in setup.py (#703) Based on the discussion in #700, this PR enables downloading pybind11 in `setup.py` without `git submodule` instead of copy-pasting pybind11 code. The downloaded pybind11 will be in `~/.triton/pybind` (like `llvm`). --- CMakeLists.txt | 5 +- python/setup.py | 76 +- python/src/pybind11/attr.h | 678 ---- python/src/pybind11/buffer_info.h | 193 -- python/src/pybind11/cast.h | 1665 ---------- python/src/pybind11/chrono.h | 225 -- python/src/pybind11/common.h | 2 - python/src/pybind11/complex.h | 74 - python/src/pybind11/detail/class.h | 742 ----- python/src/pybind11/detail/common.h | 1169 ------- python/src/pybind11/detail/descr.h | 158 - python/src/pybind11/detail/init.h | 428 --- python/src/pybind11/detail/internals.h | 562 ---- python/src/pybind11/detail/type_caster_base.h | 1010 ------ python/src/pybind11/detail/typeid.h | 65 - python/src/pybind11/eigen.h | 708 ---- python/src/pybind11/embed.h | 277 -- python/src/pybind11/eval.h | 156 - python/src/pybind11/functional.h | 130 - python/src/pybind11/gil.h | 202 -- python/src/pybind11/iostream.h | 265 -- python/src/pybind11/numpy.h | 1984 ------------ python/src/pybind11/operators.h | 201 -- python/src/pybind11/options.h | 76 - python/src/pybind11/pybind11.h | 2864 ----------------- python/src/pybind11/pytypes.h | 2392 -------------- python/src/pybind11/stl.h | 425 --- python/src/pybind11/stl/filesystem.h | 116 - python/src/pybind11/stl_bind.h | 785 ----- 29 files changed, 52 insertions(+), 17581 deletions(-) delete mode 100644 python/src/pybind11/attr.h delete mode 100644 python/src/pybind11/buffer_info.h delete mode 100644 python/src/pybind11/cast.h delete mode 100644 python/src/pybind11/chrono.h delete mode 100644 python/src/pybind11/common.h delete mode 100644 python/src/pybind11/complex.h delete mode 100644 python/src/pybind11/detail/class.h delete mode 100644 python/src/pybind11/detail/common.h delete mode 100644 python/src/pybind11/detail/descr.h delete mode 100644 python/src/pybind11/detail/init.h delete mode 100644 python/src/pybind11/detail/internals.h delete mode 100644 python/src/pybind11/detail/type_caster_base.h delete mode 100644 python/src/pybind11/detail/typeid.h delete mode 100644 python/src/pybind11/eigen.h delete mode 100644 python/src/pybind11/embed.h delete mode 100644 python/src/pybind11/eval.h delete mode 100644 python/src/pybind11/functional.h delete mode 100644 python/src/pybind11/gil.h delete mode 100644 python/src/pybind11/iostream.h delete mode 100644 python/src/pybind11/numpy.h delete mode 100644 python/src/pybind11/operators.h delete mode 100644 python/src/pybind11/options.h delete mode 100644 python/src/pybind11/pybind11.h delete mode 100644 python/src/pybind11/pytypes.h delete mode 100644 python/src/pybind11/stl.h delete mode 100644 python/src/pybind11/stl/filesystem.h delete mode 100644 python/src/pybind11/stl_bind.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e2bc7f309..deebd160c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,9 @@ endif() # Compiler flags include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +# Third-party +include_directories(${PYBIND11_INCLUDE_DIR}) + if(WIN32) SET(BUILD_SHARED_LIBS OFF) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/deps/dlfcn-win32/src) @@ -60,7 +63,7 @@ if("${LLVM_LIBRARY_DIR}" STREQUAL "") # sometimes we don't want to use llvm-config, since it may have been downloaded for some specific linux distros else() set(LLVM_LDFLAGS "-L${LLVM_LIBRARY_DIR}") - set(LLVM_LIBRARIES + set(LLVM_LIBRARIES libLLVMNVPTXCodeGen.a libLLVMNVPTXDesc.a libLLVMNVPTXInfo.a diff --git a/python/setup.py b/python/setup.py index 3940cf050..ddb3cd455 100644 --- a/python/setup.py +++ b/python/setup.py @@ -9,6 +9,7 @@ import sys import tarfile import urllib.request from distutils.version import LooseVersion +from typing import NamedTuple from setuptools import Extension, setup from setuptools.command.build_ext import build_ext @@ -28,33 +29,53 @@ def get_build_type(): return "Release" -def get_llvm(): - # tries to find system LLVM +def use_system_llvm(): + if platform.system() == "Windows": + return True versions = ['-11.0', '-11', '-11-64'] supported = ['llvm-config{v}'.format(v=v) for v in versions] paths = [distutils.spawn.find_executable(cfg) for cfg in supported] - paths = [p for p in paths if p is not None] - if paths: - return '', '' - if platform.system() == "Windows": - return '', '' - # download if nothing is installed - name = 'clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04' - dir = os.path.join(os.environ["HOME"], ".triton", "llvm") - llvm_include_dir = '{dir}/{name}/include'.format(dir=dir, name=name) - llvm_library_dir = '{dir}/{name}/lib'.format(dir=dir, name=name) - if not os.path.exists(llvm_library_dir): - os.makedirs(dir, exist_ok=True) - try: - shutil.rmtree(os.path.join(dir, name)) - except Exception: - pass - url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.1/{name}.tar.xz".format(name=name) - print('downloading and extracting ' + url + '...') - ftpstream = urllib.request.urlopen(url) - file = tarfile.open(fileobj=ftpstream, mode="r|xz") - file.extractall(path=dir) - return llvm_include_dir, llvm_library_dir + return any(p is not None for p in paths) + + +def get_thirdparty_packages(triton_cache_path): + class Package(NamedTuple): + package: str + name: str + url: str + test_file: str + include_flag: str + lib_flag: str + + packages = [ + Package("pybind11", "pybind11-2.10.0", "https://github.com/pybind/pybind11/archive/refs/tags/v2.10.0.tar.gz", "include/pybind11/pybind11.h", "PYBIND11_INCLUDE_DIR", "") + ] + if not use_system_llvm(): + # donwload LLVM if no suitable system LLVM is installed + packages.append( + Package("llvm", "clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04", "https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.1/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz", "lib", "LLVM_INCLUDE_DIRS", "LLVM_LIBRARY_DIR") + ) + + thirdparty_cmake_args = [] + for p in packages: + package_root_dir = os.path.join(triton_cache_path, p.package) + package_dir = os.path.join(package_root_dir, p.name) + test_file_path = os.path.join(package_dir, p.test_file) + if not os.path.exists(test_file_path): + try: + shutil.rmtree(package_root_dir) + except Exception: + pass + os.makedirs(package_root_dir, exist_ok=True) + print('downloading and extracting {} ...'.format(p.url)) + ftpstream = urllib.request.urlopen(p.url) + file = tarfile.open(fileobj=ftpstream, mode="r|*") + file.extractall(path=package_root_dir) + if p.include_flag: + thirdparty_cmake_args.append("-D{}={}/include".format(p.include_flag, package_dir)) + if p.lib_flag: + thirdparty_cmake_args.append("-D{}={}/lib".format(p.lib_flag, package_dir)) + return thirdparty_cmake_args class CMakeExtension(Extension): @@ -92,7 +113,8 @@ class CMakeBuild(build_ext): self.build_extension(ext) def build_extension(self, ext): - llvm_include_dir, llvm_library_dir = get_llvm() + triton_cache_path = os.path.join(os.environ["HOME"], ".triton") + thirdparty_cmake_args = get_thirdparty_packages(triton_cache_path) extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.path))) # create build directories if not os.path.exists(self.build_temp): @@ -103,12 +125,10 @@ class CMakeBuild(build_ext): "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, "-DBUILD_TUTORIALS=OFF", "-DBUILD_PYTHON_MODULE=ON", - "-DLLVM_INCLUDE_DIRS=" + llvm_include_dir, - "-DLLVM_LIBRARY_DIR=" + llvm_library_dir, # '-DPYTHON_EXECUTABLE=' + sys.executable, # '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON', "-DPYTHON_INCLUDE_DIRS=" + ";".join(python_include_dirs) - ] + ] + thirdparty_cmake_args # configuration cfg = get_build_type() build_args = ["--config", cfg] diff --git a/python/src/pybind11/attr.h b/python/src/pybind11/attr.h deleted file mode 100644 index db7cd8eff..000000000 --- a/python/src/pybind11/attr.h +++ /dev/null @@ -1,678 +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 "detail/common.h" -#include "cast.h" - -#include - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// \addtogroup annotations -/// @{ - -/// Annotation for methods -struct is_method { - handle class_; - explicit 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; - explicit scope(const handle &s) : value(s) {} -}; - -/// Annotation for documentation -struct doc { - const char *value; - explicit doc(const char *value) : value(value) {} -}; - -/// Annotation for function names -struct name { - const char *value; - explicit name(const char *value) : value(value) {} -}; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { - handle value; - explicit 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() = default; -}; - -/// 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() = default; - - /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) {} -}; - -/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that -/// may be used to customize the Python type. -/// -/// The callback is invoked immediately before `PyType_Ready`. -/// -/// Note: This is an advanced interface, and uses of it may require changes to -/// work with later versions of pybind11. You may wish to consult the -/// implementation of `make_new_python_type` in `detail/classes.h` to understand -/// the context in which the callback will be run. -struct custom_type_setup { - using callback = std::function; - - explicit custom_type_setup(callback value) : value(std::move(value)) {} - - callback value; -}; - -/// Annotation that marks a class as local to the module: -struct module_local { - const bool value; - constexpr explicit 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_; -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), - 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 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 leading positional arguments, which are terminated by a py::args or py::kwargs - /// argument or by a py::kw_only annotation. - std::uint16_t nargs_pos = 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; - - /// Custom type setup. - custom_type_setup::callback custom_type_setup_callback; - - /// 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 PY_VERSION_HEX < 0x030B0000 - dynamic_attr |= base_info->type->tp_dictoffset != 0; -#else - dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; -#endif - - if (caster) { - base_info->implicit_casts.emplace_back(type, caster); - } - } -}; - -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 check_kw_only_arg(const arg &a, function_record *r) { - if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { - pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " - "args() argument"); - } -} - -inline void append_self_arg_if_needed(function_record *r) { - if (r->is_method && r->args.empty()) { - r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); - } -} - -/// Process a keyword argument attribute (*without* a default value) -template <> -struct process_attribute : process_attribute_default { - static void init(const arg &a, function_record *r) { - append_self_arg_if_needed(r); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - - check_kw_only_arg(a, r); - } -}; - -/// 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", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); - } - - if (!a.value) { -#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) - 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?). " - "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " - "more information."); -#endif - } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - - check_kw_only_arg(a, r); - } -}; - -/// Process a keyword-only-arguments-follow pseudo argument -template <> -struct process_attribute : process_attribute_default { - static void init(const kw_only &, function_record *r) { - append_self_arg_if_needed(r); - if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { - pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " - "argument location (or omit kw_only() entirely)"); - } - r->nargs_pos = static_cast(r->args.size()); - } -}; - -/// Process a positional-only-argument maker -template <> -struct process_attribute : process_attribute_default { - static void init(const pos_only &, function_record *r) { - append_self_arg_if_needed(r); - r->nargs_pos_only = static_cast(r->args.size()); - if (r->nargs_pos_only > r->nargs_pos) { - pybind11_fail("pos_only(): cannot follow a py::args() argument"); - } - // It also can't follow a kw_only, but a static_assert in pybind11.h checks that - } -}; - -/// 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 { - static void init(const custom_type_setup &value, type_record *r) { - r->custom_type_setup_callback = value.value; - } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const is_final &, type_record *r) { r->is_final = true; } -}; - -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) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); - using expander = int[]; - (void) expander{ - 0, ((void) process_attribute::type>::init(args, r), 0)...}; - } - static void init(const Args &...args, type_record *r) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); - using expander = int[]; - (void) expander{0, - (process_attribute::type>::init(args, r), 0)...}; - } - static void precall(function_call &call) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); - using expander = int[]; - (void) expander{0, - (process_attribute::type>::precall(call), 0)...}; - } - static void postcall(function_call &call, handle fn_ret) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); - using expander = int[]; - (void) expander{ - 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; - } -}; - -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) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); - return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; -} - -PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/python/src/pybind11/buffer_info.h b/python/src/pybind11/buffer_info.h deleted file mode 100644 index 06120d556..000000000 --- a/python/src/pybind11/buffer_info.h +++ /dev/null @@ -1,193 +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 != 0)) { - // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) - this->m_view = view; - // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info &operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } - - buffer_info &operator=(buffer_info &&rhs) noexcept { - 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/python/src/pybind11/cast.h b/python/src/pybind11/cast.h deleted file mode 100644 index a0e32281b..000000000 --- a/python/src/pybind11/cast.h +++ /dev/null @@ -1,1665 +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/common.h" -#include "detail/descr.h" -#include "detail/type_caster_base.h" -#include "detail/typeid.h" -#include "pytypes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -PYBIND11_NAMESPACE_BEGIN(detail) - -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; - explicit operator std::reference_wrapper() { return cast_op(subcaster); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ -protected: \ - type value; \ - \ -public: \ - static constexpr auto name = py_name; \ - template >::value, \ - int> = 0> \ - static ::pybind11::handle cast( \ - T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ - if (!src) \ - return ::pybind11::none().release(); \ - if (policy == ::pybind11::return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); \ - delete src; \ - return h; \ - } \ - return cast(*src, policy, parent); \ - } \ - operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ - operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ - operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ - template \ - using cast_op_type = ::pybind11::detail::movable_cast_op_type - -template -using is_std_char_type = any_of, /* 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()) - || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { - return false; - } else { - handle src_or_index = src; - // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. -#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) - 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()) != 0)) { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return 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, const_name::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, const_name("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; - } - 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 */ - const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); - return true; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) { - return capsule(ptr).release(); - } - return none().inc_ref(); - } - - template - using cast_op_type = void *&; - explicit operator void *&() { return value; } - static constexpr auto name = const_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; - } - if (src.ptr() == Py_True) { - value = true; - return true; - } - if (src.ptr() == Py_False) { - value = false; - return true; - } - if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { - // (allow non-implicit conversion for numpy booleans) - - 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__" 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 = (res != 0); - return true; - } - 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, const_name("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) { - handle load_src = src; - if (!src) { - return false; - } - if (!PyUnicode_Check(load_src.ptr())) { - return load_raw(load_src); - } - - // For UTF-8 we avoid the need for a temporary `bytes` object by using - // `PyUnicode_AsUTF8AndSize`. - if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { - Py_ssize_t size = -1; - const auto *buffer - = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); - if (!buffer) { - PyErr_Clear(); - return false; - } - value = StringType(buffer, static_cast(size)); - return true; - } - - auto utfNbytes - = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), - UTF_N == 8 ? "utf-8" - : UTF_N == 16 ? "utf-16" - : "utf-32", - nullptr)); - if (!utfNbytes) { - 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); - // Skip BOM for UTF-16/32 - if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) { - buffer++; - length--; - } - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) { - loader_life_support::add_patient(utfNbytes); - } - - 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, const_name(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) { -#if !defined(PYPY_VERSION) - return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) - : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) - : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#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/bytearray 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_raw(enable_if_t::value, handle> src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (!bytes) { - pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); - } - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - if (PyByteArray_Check(src.ptr())) { - // We were passed a bytearray; accept it into a std::string or char* - // without any encoding attempt. - const char *bytearray = PyByteArray_AsString(src.ptr()); - if (!bytearray) { - pybind11_fail("Unexpected PyByteArray_AsString() failure."); - } - value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); - return true; - } - - return false; - } - - template - bool load_raw(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 = make_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); - } - - explicit operator CharT *() { - return none ? nullptr : const_cast(static_cast(str_caster).c_str()); - } - explicit operator CharT &() { - if (none) { - throw value_error("Cannot convert None to a character"); - } - - 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 (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) { - auto v0 = static_cast(value[0]); - // low bits only: 0-127 - // 0b110xxxxx - start of 2-byte sequence - // 0b1110xxxx - start of 3-byte sequence - // 0b11110xxx - start of 4-byte sequence - size_t char0_bytes = (v0 & 0x80) == 0 ? 1 - : (v0 & 0xE0) == 0xC0 ? 2 - : (v0 & 0xF0) == 0xE0 ? 3 - : 4; - - 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 (PYBIND11_SILENCE_MSVC_C4127(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 = const_name(PYBIND11_STRING_NAME); - template - using cast_op_type = pybind11::detail::cast_op_type<_T>; -}; - -// Base implementation for std::tuple and std::pair -template