Skip to content

Commit c2375af

Browse files
committed
Adding sys_string::std_format
1 parent 67df4f8 commit c2375af

File tree

6 files changed

+87
-24
lines changed

6 files changed

+87
-24
lines changed

lib/inc/sys_string/config.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@
103103
#error Your standard library does not support ranges
104104
#endif
105105

106+
//See https://github.com/llvm/llvm-project/issues/77773 for the sad story of how feature test
107+
//macros are useless with libc++
108+
#if __cpp_lib_format >= 201907L || (defined(_LIBCPP_VERSION) && __has_include(<format>))
106109

110+
#define SYS_STRING_SUPPORTS_STD_FORMAT 1
111+
112+
#endif
107113

108114
#endif

lib/inc/sys_string/impl/format.h

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,47 @@ namespace sysstr
3131
if (size == -1)
3232
return sys_string_t();
3333
buf.resize(size + 1);
34-
const int ret = vsnprintf(&buf[0], buf.size(), format, vl);
34+
const int ret = vsnprintf(buf.data(), buf.size(), format, vl);
3535
va_end(vl);
3636
if (ret <= 0)
3737
return sys_string_t();
3838
buf.resize(ret);
39-
return sys_string_t<Storage>(&buf[0], buf.size());
39+
return sys_string_t<Storage>(buf);
4040
}
41+
42+
#if SYS_STRING_SUPPORTS_STD_FORMAT
43+
44+
template<class Storage>
45+
template<class... Args>
46+
inline
47+
auto sys_string_t<Storage>::std_format(std::format_string<Args...> fmt, Args &&... args) -> sys_string_t
48+
{
49+
if constexpr (std::is_same_v<typename sys_string_t<Storage>::storage_type, char>)
50+
{
51+
sys_string_builder_t<Storage> builder;
52+
std::format_to(std::back_inserter(builder.chars()), fmt, std::forward<Args>(args)...);
53+
return builder.build();
54+
}
55+
else
56+
{
57+
return sys_string_t<Storage>(std::format(fmt, std::forward<Args>(args)...));
58+
}
59+
}
60+
61+
template<class Storage>
62+
inline
63+
auto sys_string_t<Storage>::std_vformat(std::string_view fmt, std::format_args args) -> sys_string_t
64+
{
65+
if constexpr (std::is_same_v<typename sys_string_t<Storage>::storage_type, char>)
66+
{
67+
sys_string_builder_t<Storage> builder;
68+
std::vformat_to(std::back_inserter(builder.chars()), fmt, args);
69+
return builder.build();
70+
}
71+
else
72+
{
73+
return sys_string_t<Storage>(std::vformat(fmt, args));
74+
}
75+
}
76+
#endif
4177
}

lib/inc/sys_string/impl/misc.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111

1212
#include <cstdarg>
1313

14-
#if __has_include(<format>)
15-
#include <format>
16-
#endif
17-
1814
namespace sysstr
1915
{
2016
namespace util
@@ -172,9 +168,7 @@ namespace sysstr
172168
#endif
173169
}
174170

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>))
171+
#if SYS_STRING_SUPPORTS_STD_FORMAT
178172

179173
template<class Storage> struct std::formatter<sysstr::sys_string_t<Storage>> : private std::formatter<std::string_view>
180174
{

lib/inc/sys_string/impl/unicode/utf_encoding.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ namespace sysstr
6262
template<> struct utf_encoding_value<char32_t> { static constexpr utf_encoding value = utf32; };
6363
#if SYS_STRING_WCHAR_T_IS_UTF16
6464
template<> struct utf_encoding_value<wchar_t> { static constexpr utf_encoding value = utf16; };
65+
#elif SYS_STRING_WCHAR_T_IS_UTF32
66+
template<> struct utf_encoding_value<wchar_t> { static constexpr utf_encoding value = utf32; };
6567
#endif
6668

6769
template<class T> concept has_utf_encoding = requires {

lib/inc/sys_string/sys_string.h

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#include <compare>
2323
#include <cstdarg>
2424

25+
#if __has_include(<format>)
26+
#include <format>
27+
#endif
28+
2529

2630
#define HEADER_SYS_STRING_H_INSIDE
2731

@@ -41,6 +45,10 @@ namespace sysstr
4145
copy_content,
4246
attach_pointer
4347
};
48+
49+
template<class T, class Storage>
50+
concept sys_string_or_char = std::is_same_v<std::remove_cvref_t<T>, sys_string_t<Storage>> ||
51+
std::is_same_v<std::remove_cvref_t<T>, char32_t>;
4452
}
4553

4654
namespace sysstr::util
@@ -54,21 +62,13 @@ namespace sysstr::util
5462

5563
namespace sysstr
5664
{
57-
template<class T, class Storage>
58-
concept sys_string_or_char = std::is_same_v<std::remove_cvref_t<T>, sys_string_t<Storage>> ||
59-
std::is_same_v<std::remove_cvref_t<T>, char32_t>;
60-
61-
6265
template<class Storage>
6366
class sys_string_t : public Storage
6467
{
6568
friend std::hash<sys_string_t>;
6669
friend typename Storage::char_access;
6770
private:
6871
using storage = Storage;
69-
70-
static_assert(std::ranges::random_access_range<typename storage::char_access>);
71-
7272
public:
7373
using size_type = typename storage::size_type;
7474
using storage_type = typename storage::storage_type;
@@ -77,6 +77,7 @@ namespace sysstr
7777
static constexpr size_type max_size = storage::max_size;
7878

7979
using char_access = typename storage::char_access;
80+
static_assert(std::ranges::random_access_range<char_access>);
8081

8182
template<utf_encoding Enc>
8283
class utf_access
@@ -179,8 +180,8 @@ namespace sysstr
179180

180181
template<std::ranges::contiguous_range Range>
181182
requires(has_utf_encoding<std::ranges::range_value_t<Range>>)
182-
sys_string_t(Range && val) :
183-
sys_string_t(std::ranges::data(std::forward<Range>(val)), std::ranges::size(std::forward<Range>(val)))
183+
sys_string_t(const Range & val) :
184+
sys_string_t(std::ranges::data(val), std::ranges::size(val))
184185
{}
185186

186187
sys_string_t(const typename utf32_access::iterator & first, const typename utf32_access::iterator & last):
@@ -213,8 +214,23 @@ namespace sysstr
213214
auto empty() const noexcept -> bool
214215
{ return storage::size() == 0; }
215216

216-
static auto format(const char * spec, ...) -> sys_string_t;
217-
static auto formatv(const char * spec, va_list vl) -> sys_string_t;
217+
static
218+
#ifdef __GNUC__
219+
__attribute__((format(printf, 1, 2)))
220+
#endif
221+
auto format(const char * spec, ...) -> sys_string_t;
222+
223+
static
224+
#ifdef __GNUC__
225+
__attribute__((format(printf, 1, 0)))
226+
#endif
227+
auto formatv(const char * spec, va_list vl) -> sys_string_t;
228+
229+
#if SYS_STRING_SUPPORTS_STD_FORMAT
230+
template<class... Args>
231+
static auto std_format(std::format_string<Args...> fmt, Args &&... args) -> sys_string_t;
232+
static auto std_vformat(std::string_view fmt, std::format_args args) -> sys_string_t;
233+
#endif
218234

219235
template<class S>
220236
friend auto operator<<(std::ostream & str, const sys_string_t<S> & val) -> std::ostream &;

test/test_general.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -649,14 +649,23 @@ TEST_CASE( "ostream", "[general]" ) {
649649
#endif
650650
}
651651

652-
#if __cpp_lib_format >= 201907L || (defined(_LIBCPP_VERSION) && __has_include(<format>))
653652

654-
TEST_CASE( "std::format", "[general]" ) {
653+
654+
TEST_CASE( "format", "[general]" ) {
655+
656+
CHECK(sys_string::format("%d", 5) == S("5"));
657+
658+
#if SYS_STRING_SUPPORTS_STD_FORMAT
659+
CHECK(sys_string::std_format("{}", 5) == S("5"));
660+
int i = 5;
661+
CHECK(sys_string::std_vformat("{}", std::make_format_args(i)) == S("5"));
655662

656663
CHECK(std::format("{0}", S("a🧡bc")) == "a🧡bc");
657664
#if SYS_STRING_WCHAR_T_IS_UTF16 || SYS_STRING_WCHAR_T_IS_UTF32
658665
CHECK(std::format(L"{0}", S("a🧡bc")) == L"a🧡bc");
659666
#endif
667+
668+
#endif
669+
660670
}
661671

662-
#endif

0 commit comments

Comments
 (0)