blob: 99e5eaf411f9b3a467c0c79a8022681a09d1c50a [file] [log] [blame]
// { dg-do run { target c++23 } }
// { dg-options "-fexec-charset=UTF-8" }
// { dg-timeout-factor 2 }
#include <format>
#include <forward_list>
#include <span>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
#include <vector>
template<typename... Args>
bool
is_format_string_for(const char* str, Args&&... args)
{
try {
(void) std::vformat(str, std::make_format_args(args...));
return true;
} catch (const std::format_error&) {
return false;
}
}
template<typename... Args>
bool
is_format_string_for(const wchar_t* str, Args&&... args)
{
try {
(void) std::vformat(str, std::make_wformat_args(args...));
return true;
} catch (const std::format_error&) {
return false;
}
}
template<typename Rg, typename CharT>
bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg)
{
using V = std::remove_cvref_t<std::ranges::range_reference_t<Rg>>;
std::range_formatter<V, CharT> fmt;
std::basic_format_parse_context<CharT> pc(spec);
try {
(void)fmt.parse(pc);
return true;
} catch (const std::format_error&) {
return false;
}
}
#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
#define WIDEN(S) WIDEN_(CharT, S)
void
test_format_string()
{
// only CharT value types are supported
VERIFY( !is_range_formatter_spec_for(L"s", std::vector<char>()) );
VERIFY( !is_format_string_for(L"{:s}", std::vector<char>()) );
VERIFY( !is_range_formatter_spec_for(L"s", std::vector<char>()) );
VERIFY( !is_format_string_for(L"{:s}", std::vector<char>()) );
VERIFY( !is_range_formatter_spec_for("s", std::vector<int>()) );
VERIFY( !is_format_string_for("{:s}", std::vector<int>()) );
// invalid format stringss
VERIFY( !is_range_formatter_spec_for("?", std::vector<char>()) );
VERIFY( !is_format_string_for("{:?}", std::vector<char>()) );
VERIFY( !is_range_formatter_spec_for("ns", std::vector<char>()) );
VERIFY( !is_format_string_for("{:ns}", std::vector<char>()) );
VERIFY( !is_range_formatter_spec_for("s:", std::vector<char>()) );
VERIFY( !is_format_string_for("{:s:}", std::vector<char>()) );
// precision is not supported, even for s
VERIFY( !is_range_formatter_spec_for(".10s", std::vector<char>()) );
VERIFY( !is_format_string_for("{:.10s}", std::vector<char>()) );
VERIFY( !is_format_string_for("{:.{}s}", std::vector<char>(), 10) );
// width needs to be integer type
VERIFY( !is_format_string_for("{:{}s}", std::vector<char>(), 1.0f) );
}
template<typename Range>
void test_output()
{
using CharT = std::ranges::range_value_t<Range>;
auto makeRange = [](std::basic_string<CharT>& s) {
return Range(s.data(), s.data() + s.size());
};
std::basic_string<CharT> res;
size_t size = 0;
std::basic_string<CharT> s1 = WIDEN("abcd");
res = std::format(WIDEN("{}"), makeRange(s1));
VERIFY( res == WIDEN("['a', 'b', 'c', 'd']") );
res = std::format(WIDEN("{::}"), makeRange(s1));
VERIFY( res == WIDEN("[a, b, c, d]") );
res = std::format(WIDEN("{:s}"), makeRange(s1));
VERIFY( res == WIDEN("abcd") );
res = std::format(WIDEN("{:?s}"), makeRange(s1));
VERIFY( res == WIDEN(R"("abcd")") );
res = std::format(WIDEN("{:3s}"), makeRange(s1));
VERIFY( res == WIDEN("abcd") );
res = std::format(WIDEN("{:7s}"), makeRange(s1));
VERIFY( res == WIDEN("abcd ") );
res = std::format(WIDEN("{:{}s}"), makeRange(s1), 7);
VERIFY( res == WIDEN("abcd ") );
res = std::format(WIDEN("{1:{0}s}"), 7, makeRange(s1));
VERIFY( res == WIDEN("abcd ") );
res = std::format(WIDEN("{:*>6s}"), makeRange(s1));
VERIFY( res == WIDEN("**abcd") );
res = std::format(WIDEN("{:-<5s}"), makeRange(s1));
VERIFY( res == WIDEN("abcd-") );
res = std::format(WIDEN("{:=^8s}"), makeRange(s1));
VERIFY( res == WIDEN("==abcd==") );
std::basic_string<CharT> s2(512, static_cast<CharT>('a'));
res = std::format(WIDEN("{:=^8s}"), makeRange(s2));
VERIFY( res == s2 );
size = std::formatted_size(WIDEN("{:s}"), makeRange(s1));
VERIFY( size == 4 );
size = std::formatted_size(WIDEN("{:3s}"), makeRange(s1));
VERIFY( size == 4 );
size = std::formatted_size(WIDEN("{:7s}"), makeRange(s1));
VERIFY( size == 7 );
size = std::formatted_size(WIDEN("{:s}"), makeRange(s2));
VERIFY( size == 512 );
}
template<typename CharT>
struct cstr_view
{
cstr_view() = default;
explicit cstr_view(CharT* f, CharT* l)
: ptr(f)
{ VERIFY(!*l); }
struct sentinel
{
friend constexpr
bool operator==(CharT const* ptr, sentinel) noexcept
{ return !*ptr; }
};
constexpr
CharT* begin() const noexcept
{ return ptr; };
static constexpr
sentinel end() noexcept
{ return {}; }
private:
CharT* ptr = "";
};
template<typename CharT>
void
test_outputs()
{
using namespace __gnu_test;
test_output<std::vector<CharT>>();
test_output<std::span<CharT>>();
test_output<cstr_view<CharT>>();
test_output<test_forward_range<CharT>>();
test_output<test_forward_sized_range<CharT>>();
test_output<test_input_range<CharT>>();
test_output<test_input_sized_range<CharT>>();
test_output<test_range_nocopy<CharT, input_iterator_wrapper_nocopy>>();
test_output<test_sized_range<CharT, input_iterator_wrapper_nocopy>>();
test_output<std::span<const CharT>>();
test_output<cstr_view<const CharT>>();
test_output<test_forward_range<const CharT>>();
static_assert(!std::formattable<std::span<volatile CharT>, CharT>);
static_assert(!std::formattable<std::span<const volatile CharT>, CharT>);
}
void
test_nested()
{
std::string_view s1 = "str1";
std::string_view s2 = "str2";
std::vector<std::string> vs;
vs.emplace_back(s1);
vs.emplace_back(s2);
VERIFY( std::format("{}", vs) == R"(["str1", "str2"])" );
VERIFY( std::format("{:}", vs) == R"(["str1", "str2"])" );
VERIFY( std::format("{::?}", vs) == R"(["str1", "str2"])" );
VERIFY( std::format("{::}", vs) == R"([str1, str2])" );
std::vector<std::vector<char>> vv;
vv.emplace_back(s1.begin(), s1.end());
vv.emplace_back(s2.begin(), s2.end());
std::string_view escaped = R"([['s', 't', 'r', '1'], ['s', 't', 'r', '2']])";
VERIFY( std::format("{}", vv) == escaped );
VERIFY( std::format("{:}", vv) == escaped );
VERIFY( std::format("{::}", vv) == escaped );
VERIFY( std::format("{:::?}", vv) == escaped );
VERIFY( std::format("{:::}", vv) == R"([[s, t, r, 1], [s, t, r, 2]])" );
VERIFY( std::format("{::s}", vv) == R"([str1, str2])" );
VERIFY( std::format("{::?s}", vv) == R"(["str1", "str2"])" );
}
bool strip_quotes(std::string_view& v)
{
if (!v.starts_with('"') || !v.ends_with('"'))
return false;
v.remove_prefix(1);
v.remove_suffix(1);
return true;
}
bool strip_prefix(std::string_view& v, size_t n, char c)
{
size_t pos = v.find_first_not_of(c);
if (pos == std::string_view::npos)
pos = v.size();
if (pos != n)
return false;
v.remove_prefix(n);
return true;
}
void test_padding()
{
std::string res;
std::string_view resv;
// width is 3, size is 15
std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323";
in += in; // width is 6, size is 30
in += in; // width is 12, size is 60
in += in; // width is 24, size is 120
in += in; // width is 48, size is 240
in += in; // width is 96, size is 480
in += in; // width is 192, size is 960
std::forward_list<char> lc(std::from_range, in);
resv = res = std::format("{:s}", lc);
VERIFY( resv == in );
resv = res = std::format("{:*>10s}", lc);
VERIFY( resv == in );
resv = res = std::format("{:*>240s}", lc);
VERIFY( strip_prefix(resv, 48, '*') );
VERIFY( resv == in );
resv = res = std::format("{:?s}", lc);
VERIFY( strip_quotes(resv) );
VERIFY( resv == in );
resv = res = std::format("{:*>10?s}", lc);
VERIFY( strip_quotes(resv) );
VERIFY( resv == in );
resv = res = std::format("{:*>240?s}", lc);
VERIFY( strip_prefix(resv, 46, '*') );
VERIFY( strip_quotes(resv) );
VERIFY( resv == in );
}
int main()
{
test_format_string();
test_outputs<char>();
test_outputs<wchar_t>();
test_nested();
}