mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Support std::string_view when compiled under C++17
This commit is contained in:
parent
220a77f5cd
commit
f42af24a7d
@ -116,6 +116,9 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
|
|||||||
+------------------------------------+---------------------------+-------------------------------+
|
+------------------------------------+---------------------------+-------------------------------+
|
||||||
| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` |
|
| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` |
|
||||||
+------------------------------------+---------------------------+-------------------------------+
|
+------------------------------------+---------------------------+-------------------------------+
|
||||||
|
| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` |
|
||||||
|
| ``std::u16string_view``, etc. | | |
|
||||||
|
+------------------------------------+---------------------------+-------------------------------+
|
||||||
| ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` |
|
| ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` |
|
||||||
+------------------------------------+---------------------------+-------------------------------+
|
+------------------------------------+---------------------------+-------------------------------+
|
||||||
| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` |
|
| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` |
|
||||||
|
@ -287,6 +287,15 @@ expressed as a single Unicode code point
|
|||||||
no way to capture them in a C++ character type.
|
no way to capture them in a C++ character type.
|
||||||
|
|
||||||
|
|
||||||
|
C++17 string views
|
||||||
|
==================
|
||||||
|
|
||||||
|
C++17 string views are automatically supported when compiling in C++17 mode.
|
||||||
|
They follow the same rules for encoding and decoding as the corresponding STL
|
||||||
|
string type (for example, a ``std::u16string_view`` argument will be passed
|
||||||
|
UTF-16-encoded data, and a returned ``std::string_view`` will be decoded as
|
||||||
|
UTF-8).
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
@ -18,6 +18,19 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#if defined(PYBIND11_CPP17)
|
||||||
|
# if defined(__has_include)
|
||||||
|
# if __has_include(<string_view>)
|
||||||
|
# define PYBIND11_HAS_STRING_VIEW
|
||||||
|
# endif
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define PYBIND11_HAS_STRING_VIEW
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
NAMESPACE_BEGIN(pybind11)
|
||||||
NAMESPACE_BEGIN(detail)
|
NAMESPACE_BEGIN(detail)
|
||||||
// Forward declarations:
|
// Forward declarations:
|
||||||
@ -1003,10 +1016,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper class for UTF-{8,16,32} C++ stl strings:
|
// Helper class for UTF-{8,16,32} C++ stl strings:
|
||||||
template <typename CharT, class Traits, class Allocator>
|
template <typename StringType, bool IsView = false> struct string_caster {
|
||||||
struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_std_char_type<CharT>::value>> {
|
using CharT = typename StringType::value_type;
|
||||||
|
|
||||||
// Simplify life by being able to assume standard char sizes (the standard only guarantees
|
// Simplify life by being able to assume standard char sizes (the standard only guarantees
|
||||||
// minimums), but Python requires exact sizes
|
// minimums, but Python requires exact sizes)
|
||||||
static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
|
static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
|
||||||
static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
|
static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
|
||||||
static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
|
static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
|
||||||
@ -1015,8 +1029,6 @@ struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_s
|
|||||||
"Unsupported wchar_t size != 2/4");
|
"Unsupported wchar_t size != 2/4");
|
||||||
static constexpr size_t UTF_N = 8 * sizeof(CharT);
|
static constexpr size_t UTF_N = 8 * sizeof(CharT);
|
||||||
|
|
||||||
using StringType = std::basic_string<CharT, Traits, Allocator>;
|
|
||||||
|
|
||||||
bool load(handle src, bool) {
|
bool load(handle src, bool) {
|
||||||
#if PY_MAJOR_VERSION < 3
|
#if PY_MAJOR_VERSION < 3
|
||||||
object temp;
|
object temp;
|
||||||
@ -1050,11 +1062,16 @@ struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_s
|
|||||||
size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
|
size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
|
||||||
if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32
|
if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32
|
||||||
value = StringType(buffer, length);
|
value = StringType(buffer, length);
|
||||||
|
|
||||||
|
// If we're loading a string_view we need to keep the encoded Python object alive:
|
||||||
|
if (IsView)
|
||||||
|
view_into = std::move(utfNbytes);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) {
|
static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) {
|
||||||
const char *buffer = reinterpret_cast<const char *>(src.c_str());
|
const char *buffer = reinterpret_cast<const char *>(src.data());
|
||||||
ssize_t nbytes = ssize_t(src.size() * sizeof(CharT));
|
ssize_t nbytes = ssize_t(src.size() * sizeof(CharT));
|
||||||
handle s = decode_utfN(buffer, nbytes);
|
handle s = decode_utfN(buffer, nbytes);
|
||||||
if (!s) throw error_already_set();
|
if (!s) throw error_already_set();
|
||||||
@ -1064,6 +1081,8 @@ struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_s
|
|||||||
PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME));
|
PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
object view_into;
|
||||||
|
|
||||||
static handle decode_utfN(const char *buffer, ssize_t nbytes) {
|
static handle decode_utfN(const char *buffer, ssize_t nbytes) {
|
||||||
#if !defined(PYPY_VERSION)
|
#if !defined(PYPY_VERSION)
|
||||||
return
|
return
|
||||||
@ -1101,6 +1120,16 @@ private:
|
|||||||
bool load_bytes(enable_if_t<sizeof(C) != 1, handle>) { return false; }
|
bool load_bytes(enable_if_t<sizeof(C) != 1, handle>) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename CharT, class Traits, class Allocator>
|
||||||
|
struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_std_char_type<CharT>::value>>
|
||||||
|
: string_caster<std::basic_string<CharT, Traits, Allocator>> {};
|
||||||
|
|
||||||
|
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||||
|
template <typename CharT, class Traits>
|
||||||
|
struct type_caster<std::basic_string_view<CharT, Traits>, enable_if_t<is_std_char_type<CharT>::value>>
|
||||||
|
: string_caster<std::basic_string_view<CharT, Traits>, true> {};
|
||||||
|
#endif
|
||||||
|
|
||||||
// Type caster for C-style strings. We basically use a std::string type caster, but also add the
|
// 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).
|
// ability to use None as a nullptr char* (which the string caster doesn't allow).
|
||||||
template <typename CharT> struct type_caster<CharT, enable_if_t<is_std_char_type<CharT>::value>> {
|
template <typename CharT> struct type_caster<CharT, enable_if_t<is_std_char_type<CharT>::value>> {
|
||||||
|
@ -555,6 +555,21 @@ test_initializer python_types([](py::module &m) {
|
|||||||
m.def("nodefer_none_optional", [](py::none) { return false; });
|
m.def("nodefer_none_optional", [](py::none) { return false; });
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||||
|
m.attr("has_string_view") = true;
|
||||||
|
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
||||||
|
m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
|
||||||
|
m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
|
||||||
|
m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
|
||||||
|
m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
|
||||||
|
m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
|
||||||
|
m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); });
|
||||||
|
m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); });
|
||||||
|
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); });
|
||||||
|
#else
|
||||||
|
m.attr("has_string_view") = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
m.def("return_capsule_with_destructor",
|
m.def("return_capsule_with_destructor",
|
||||||
[]() {
|
[]() {
|
||||||
py::print("creating capsule");
|
py::print("creating capsule");
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import pybind11_tests
|
import pybind11_tests
|
||||||
|
|
||||||
from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
|
from pybind11_tests import (ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional,
|
||||||
|
has_string_view)
|
||||||
|
|
||||||
|
|
||||||
def test_repr():
|
def test_repr():
|
||||||
@ -558,6 +559,48 @@ def test_bytes_to_string():
|
|||||||
assert string_length(u'💩'.encode("utf8")) == 4
|
assert string_length(u'💩'.encode("utf8")) == 4
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not has_string_view, reason='no <string_view>')
|
||||||
|
def test_string_view(capture):
|
||||||
|
"""Tests support for C++17 string_view arguments and return values"""
|
||||||
|
from pybind11_tests import (string_view_print, string_view16_print, string_view32_print,
|
||||||
|
string_view_chars, string_view16_chars, string_view32_chars,
|
||||||
|
string_view_return, string_view16_return, string_view32_return)
|
||||||
|
|
||||||
|
assert string_view_chars("Hi") == [72, 105]
|
||||||
|
assert string_view_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82]
|
||||||
|
assert string_view16_chars("Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82]
|
||||||
|
assert string_view32_chars("Hi 🎂") == [72, 105, 32, 127874]
|
||||||
|
|
||||||
|
assert string_view_return() == "utf8 secret 🎂"
|
||||||
|
assert string_view16_return() == "utf16 secret 🎂"
|
||||||
|
assert string_view32_return() == "utf32 secret 🎂"
|
||||||
|
|
||||||
|
with capture:
|
||||||
|
string_view_print("Hi")
|
||||||
|
string_view_print("utf8 🎂")
|
||||||
|
string_view16_print("utf16 🎂")
|
||||||
|
string_view32_print("utf32 🎂")
|
||||||
|
|
||||||
|
assert capture == """
|
||||||
|
Hi 2
|
||||||
|
utf8 🎂 9
|
||||||
|
utf16 🎂 8
|
||||||
|
utf32 🎂 7
|
||||||
|
"""
|
||||||
|
|
||||||
|
with capture:
|
||||||
|
string_view_print("Hi, ascii")
|
||||||
|
string_view_print("Hi, utf8 🎂")
|
||||||
|
string_view16_print("Hi, utf16 🎂")
|
||||||
|
string_view32_print("Hi, utf32 🎂")
|
||||||
|
assert capture == """
|
||||||
|
Hi, ascii 9
|
||||||
|
Hi, utf8 🎂 13
|
||||||
|
Hi, utf16 🎂 12
|
||||||
|
Hi, utf32 🎂 11
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_builtins_cast_return_none():
|
def test_builtins_cast_return_none():
|
||||||
"""Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None"""
|
"""Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None"""
|
||||||
import pybind11_tests as m
|
import pybind11_tests as m
|
||||||
|
Loading…
Reference in New Issue
Block a user