Previously, libcxx forced all strings created during constant evaluation to point to allocated memory. That was done due to implementation difficultites, but it turns out not to be necessary. This patch permits the use of SSO strings during constant evaluation, and also simplifies the implementation. This does have a downside in terms of enabling users to accidentally write non-portable code, however, which I've documented in UsingLibcxx.rst. In particular, whether `constinit std::string x = "...";` will successfully compile now depends on whether the string is smaller than the SSO capacity -- in libc++, up to 22 bytes on 64-bit platforms, and up to 10 bytes on 32-bit platforms. By comparison, libstdc++ and MSVC have an SSO capacity of 15 bytes, except that in libstdc++, constant-initialized strings cannot be used as function-locals because the object contains a pointer to itself. Closes #68434
159 lines
5.1 KiB
C++
159 lines
5.1 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// test:
|
|
|
|
// template <class charT, class traits, class Allocator>
|
|
// basic_string<charT, traits, Allocator>
|
|
// to_string(charT zero = charT('0'), charT one = charT('1')) const; // constexpr since C++23
|
|
//
|
|
// template <class charT, class traits>
|
|
// basic_string<charT, traits, allocator<charT> > to_string() const; // constexpr since C++23
|
|
//
|
|
// template <class charT>
|
|
// basic_string<charT, char_traits<charT>, allocator<charT> > to_string() const; // constexpr since C++23
|
|
//
|
|
// basic_string<char, char_traits<char>, allocator<char> > to_string() const; // constexpr since C++23
|
|
|
|
#include <bitset>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <memory> // for std::allocator
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "../bitset_test_cases.h"
|
|
#include "test_macros.h"
|
|
|
|
template <class CharT, std::size_t N>
|
|
TEST_CONSTEXPR_CXX23 void check_equal(std::basic_string<CharT> const& s, std::bitset<N> const& b, CharT zero, CharT one) {
|
|
assert(s.size() == b.size());
|
|
for (std::size_t i = 0; i < b.size(); ++i) {
|
|
if (b[i]) {
|
|
assert(s[b.size() - 1 - i] == one);
|
|
} else {
|
|
assert(s[b.size() - 1 - i] == zero);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <std::size_t N>
|
|
TEST_CONSTEXPR_CXX23 bool test_to_string() {
|
|
std::vector<std::bitset<N> > const cases = get_test_cases<N>();
|
|
for (std::size_t c = 0; c != cases.size(); ++c) {
|
|
std::bitset<N> const v = cases[c];
|
|
{
|
|
std::string s = v.template to_string<char>();
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.to_string();
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.template to_string<char>('0');
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.to_string('0');
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.template to_string<char>('0', '1');
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.to_string('0', '1');
|
|
check_equal(s, v, '0', '1');
|
|
}
|
|
{
|
|
std::string s = v.to_string('x', 'y');
|
|
check_equal(s, v, 'x', 'y');
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
|
template <std::size_t N>
|
|
TEST_CONSTEXPR_CXX23 bool test_to_string_wchar() {
|
|
std::vector<std::bitset<N> > const cases = get_test_cases<N>();
|
|
for (std::size_t c = 0; c != cases.size(); ++c) {
|
|
std::bitset<N> const v = cases[c];
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >();
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t> >();
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >('0');
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t> >('0');
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >('0', '1');
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
{
|
|
std::wstring s = v.template to_string<wchar_t, std::char_traits<wchar_t> >('0', '1');
|
|
check_equal(s, v, L'0', L'1');
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
int main(int, char**) {
|
|
test_to_string<0>();
|
|
test_to_string<1>();
|
|
test_to_string<31>();
|
|
test_to_string<32>();
|
|
test_to_string<33>();
|
|
test_to_string<63>();
|
|
test_to_string<64>();
|
|
test_to_string<65>();
|
|
test_to_string<1000>(); // not in constexpr because of constexpr evaluation step limits
|
|
#if TEST_STD_VER > 20
|
|
static_assert(test_to_string<0>());
|
|
static_assert(test_to_string<1>());
|
|
static_assert(test_to_string<31>());
|
|
static_assert(test_to_string<32>());
|
|
static_assert(test_to_string<33>());
|
|
static_assert(test_to_string<63>());
|
|
static_assert(test_to_string<64>());
|
|
static_assert(test_to_string<65>());
|
|
#endif
|
|
|
|
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
|
test_to_string_wchar<0>();
|
|
test_to_string_wchar<1>();
|
|
test_to_string_wchar<31>();
|
|
test_to_string_wchar<32>();
|
|
test_to_string_wchar<33>();
|
|
test_to_string_wchar<63>();
|
|
test_to_string_wchar<64>();
|
|
test_to_string_wchar<65>();
|
|
test_to_string_wchar<1000>(); // not in constexpr because of constexpr evaluation step limits
|
|
#if TEST_STD_VER > 20
|
|
static_assert(test_to_string_wchar<0>());
|
|
static_assert(test_to_string_wchar<1>());
|
|
static_assert(test_to_string_wchar<31>());
|
|
static_assert(test_to_string_wchar<32>());
|
|
static_assert(test_to_string_wchar<33>());
|
|
static_assert(test_to_string_wchar<63>());
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|