Skip to content

Commit 6eb7fd3

Browse files
committed
Adding support for std::format
1 parent 44caf7e commit 6eb7fd3

File tree

5 files changed

+131
-21
lines changed

5 files changed

+131
-21
lines changed

lib/inc/sys_string/config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
#error Please define pointer size for your platform
7272
#endif
7373

74+
#if defined(__STDC_ISO_10646__) && !SYS_STRING_WCHAR_T_IS_UTF16
75+
#define SYS_STRING_WCHAR_T_IS_UTF32 1
76+
#endif
77+
78+
7479
#if defined(_MSC_VER)
7580
#define SYS_STRING_FORCE_INLINE __forceinline
7681
#elif defined(__GNUC__)

lib/inc/sys_string/impl/format.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ namespace sysstr
3838
buf.resize(ret);
3939
return sys_string_t<Storage>(&buf[0], buf.size());
4040
}
41-
}
41+
}

lib/inc/sys_string/impl/misc.h

Lines changed: 114 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
#include <cstdarg>
1313

14+
#if __has_include(<format>)
15+
#include <format>
16+
#endif
17+
1418
namespace sysstr
1519
{
1620
namespace util
@@ -80,43 +84,138 @@ namespace sysstr
8084

8185
return std::ranges::equal(test.begin(), test.end(), access.begin(), access.begin() + test_size);
8286
}
87+
88+
template<class Storage, class Func>
89+
inline
90+
decltype(auto) with_output_string(const sys_string_t<Storage> & val, Func func)
91+
{
92+
if constexpr (std::ranges::contiguous_range<typename sys_string_t<Storage>::char_access> &&
93+
std::is_same_v<typename sys_string_t<Storage>::storage_type, char>)
94+
{
95+
typename sysstr::sys_string_t<Storage>::char_access access(val);
96+
return func(std::string_view(access.data(), access.size()));
97+
}
98+
else
99+
{
100+
std::string out;
101+
typename sysstr::sys_string_t<Storage>::utf8_access(val).each([&out](char c) {
102+
out += c;
103+
});
104+
return func(std::string_view(out));
105+
}
106+
}
107+
108+
#if SYS_STRING_WCHAR_T_IS_UTF16
109+
template<class Storage, class Func>
110+
inline
111+
decltype(auto) with_output_wstring(const sys_string_t<Storage> & val, Func func)
112+
{
113+
if constexpr (std::ranges::contiguous_range<typename sys_string_t<Storage>::char_access> &&
114+
std::is_same_v<typename sys_string_t<Storage>::storage_type, char16_t>)
115+
{
116+
typename sysstr::sys_string_t<Storage>::char_access access(val);
117+
return func(std::wstring_view((const wchar_t *)access.data(), access.size()));
118+
}
119+
else
120+
{
121+
std::wstring out;
122+
typename sysstr::sys_string_t<Storage>::utf16_access(val).each([&out](char16_t c) {
123+
out += wchar_t(c);
124+
});
125+
return func(std::wstring_view(out));
126+
}
127+
}
128+
#elif SYS_STRING_WCHAR_T_IS_UTF32
129+
template<class Storage, class Func>
130+
inline
131+
decltype(auto) with_output_wstring(const sys_string_t<Storage> & val, Func func)
132+
{
133+
if constexpr (std::ranges::contiguous_range<typename sys_string_t<Storage>::char_access> &&
134+
std::is_same_v<typename sys_string_t<Storage>::storage_type, char32_t>)
135+
{
136+
typename sysstr::sys_string_t<Storage>::char_access access(val);
137+
return func(std::wstring_view((const wchar_t *)access.data(), access.size()));
138+
}
139+
else
140+
{
141+
std::wstring out;
142+
typename sysstr::sys_string_t<Storage>::utf32_access(val).each([&out](char32_t c) {
143+
out += wchar_t(c);
144+
});
145+
return func(std::wstring_view(out));
146+
}
147+
}
148+
#endif
83149
}
84150

85151

86152
template<class Storage>
87153
inline
88154
auto operator<<(std::ostream & str, const sys_string_t<Storage> & val) -> std::ostream &
89-
{
90-
for(char c: typename sys_string_t<Storage>::utf8_view(val))
91-
str << c;
92-
return str;
155+
{
156+
return util::with_output_string(val, [&](auto view) -> std::ostream & {
157+
return str << view;
158+
});
93159
}
94160

95-
#if defined(_WIN32)
161+
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
96162

97163
template<class Storage>
98164
inline
99165
auto operator<<(std::wostream & str, const sys_string_t<Storage> & val) -> std::wostream &
166+
{
167+
return util::with_output_wstring(val, [&](auto view) -> std::wostream & {
168+
return str << view;
169+
});
170+
}
171+
172+
#endif
173+
}
174+
175+
//See https://github.com/llvm/llvm-project/issues/77773 for the sad story of how feature test
176+
//macros are useless with libc++
177+
#if __cpp_lib_format >= 201907L || (defined(_LIBCPP_VERSION) && __has_include(<format>))
178+
179+
template<class Storage> struct std::formatter<sysstr::sys_string_t<Storage>> : private std::formatter<std::string_view>
180+
{
181+
using super = std::formatter<std::string_view>;
182+
183+
using super::formatter;
184+
using super::parse;
185+
186+
template <typename FormatContext>
187+
auto format(const sysstr::sys_string_t<Storage> & str, FormatContext & ctx) const -> decltype(ctx.out())
100188
{
101-
for(char16_t c: typename sys_string_t<Storage>::utf16_view(val))
102-
str << wchar_t(c);
103-
return str;
189+
return sysstr::util::with_output_string(str, [&](auto view) {
190+
return super::format(view, ctx);
191+
});
104192
}
193+
};
105194

106-
#elif defined(__STDC_ISO_10646__)
195+
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
107196

108-
template<class Storage>
109-
inline
110-
auto operator<<(std::wostream & str, const sys_string_t<Storage> & val) -> std::wostream &
197+
template<class Storage> struct std::formatter<sysstr::sys_string_t<Storage>, wchar_t> : private std::formatter<std::wstring_view, wchar_t>
198+
{
199+
using super = std::formatter<std::wstring_view, wchar_t>;
200+
201+
using super::formatter;
202+
using super::parse;
203+
204+
template <typename FormatContext>
205+
auto format(const sysstr::sys_string_t<Storage> & str, FormatContext & ctx) const -> decltype(ctx.out())
111206
{
112-
for(char32_t c: typename sys_string_t<Storage>::utf32_view(val))
113-
str << wchar_t(c);
114-
return str;
207+
return sysstr::util::with_output_wstring(str, [&](auto view) {
208+
return super::format(view, ctx);
209+
});
115210
}
211+
};
116212

117213
#endif
118214

215+
#endif
119216

217+
namespace sysstr
218+
{
120219
template<class Storage>
121220
inline
122221
auto sys_string_t<Storage>::to_lower() const -> sys_string_t<Storage>

lib/inc/sys_string/sys_string.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <ostream>
2121
#include <limits>
2222
#include <compare>
23-
#include <stdarg.h>
23+
#include <cstdarg>
2424

2525

2626
#define HEADER_SYS_STRING_H_INSIDE
@@ -218,7 +218,7 @@ namespace sysstr
218218

219219
template<class S>
220220
friend auto operator<<(std::ostream & str, const sys_string_t<S> & val) -> std::ostream &;
221-
#if defined(_WIN32) || defined(__STDC_ISO_10646__)
221+
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
222222
template<class S>
223223
friend auto operator<<(std::wostream & str, const sys_string_t<S> & val) -> std::wostream &;
224224
#endif

test/test_general.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ TEST_CASE( "ostream", "[general]" ) {
628628
CHECK(stream.str() == "a🧡bc");
629629
}
630630

631-
#if defined(_WIN32) || defined(__STDC_ISO_10646__)
631+
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
632632
{
633633
std::wostringstream stream;
634634
stream << sys_string();
@@ -649,8 +649,14 @@ TEST_CASE( "ostream", "[general]" ) {
649649
#endif
650650
}
651651

652-
TEST_CASE( "utf views", "[general]" ) {
652+
#if __cpp_lib_format >= 201907L || (defined(_LIBCPP_VERSION) && __has_include(<format>))
653653

654+
TEST_CASE( "std::format", "[general]" ) {
654655

656+
CHECK(std::format("{0}", S("a🧡bc")) == "a🧡bc");
657+
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
658+
CHECK(std::format(L"{0}", S("a🧡bc")) == L"a🧡bc");
659+
#endif
660+
}
655661

656-
}
662+
#endif

0 commit comments

Comments
 (0)