Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
StringFormat.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/AlignedStorage.h"
5#include "../Foundation/Internal/IGrowableBuffer.h"
6#include "../Strings/StringView.h"
7#include "StringsExport.h"
8
9namespace SC
10{
11struct Console;
12
13struct StringFormatOutput;
14template <typename T>
15struct StringFormatterFor
16{
17 static bool format(StringFormatOutput& data, const StringSpan specifier, const T& value)
18 {
19 return StringFormatterFor<decltype(value.view())>::format(data, specifier, value.view());
20 };
21};
22
26struct SC_STRINGS_EXPORT StringFormatOutput
27{
28 StringFormatOutput(StringEncoding encoding, IGrowableBuffer& buffer) : encoding(encoding), growableBuffer(&buffer)
29 {}
30
35 StringFormatOutput(StringEncoding encoding, Console& destination, bool useStdOut);
36
40 [[nodiscard]] bool append(StringView text);
41
44
47
50 [[nodiscard]] bool onFormatSucceeded();
51
52 private:
53 IGrowableBuffer* growableBuffer = nullptr;
54 StringEncoding encoding;
55 bool useStdOut = true;
56
57 Console* console = nullptr;
58 size_t backupSize = 0;
59};
60
82template <typename RangeIterator>
84{
91 template <typename... Types>
92 [[nodiscard]] static bool format(StringFormatOutput& data, StringView fmt, Types&&... args);
93
94 private:
95 struct Implementation;
96};
98
99} // namespace SC
100
101//-----------------------------------------------------------------------------------------------------------------------
102// Implementations Details
103//-----------------------------------------------------------------------------------------------------------------------
104template <typename RangeIterator>
105struct SC::StringFormat<RangeIterator>::Implementation
106{
107 template <int Total, int N, typename T, typename... Rest>
108 static bool formatArgument(StringFormatOutput& data, StringView specifier, int position, T&& arg, Rest&&... rest)
109 {
110 if (position == Total - N)
111 {
112 using First = typename TypeTraits::RemoveConst<typename TypeTraits::RemoveReference<T>::type>::type;
113 return StringFormatterFor<First>::format(data, specifier, arg);
114 }
115 else
116 {
117 return formatArgument<Total, N - 1>(data, specifier, position, forward<Rest>(rest)...);
118 }
119 }
120
121 template <int Total, int N, typename... Args>
122 static typename SC::TypeTraits::EnableIf<sizeof...(Args) == 0, bool>::type formatArgument(StringFormatOutput&,
123 StringView, int, Args...)
124 {
125 return false;
126 }
127
128 template <typename... Types>
129 static bool parsePosition(StringFormatOutput& data, RangeIterator& it, int32_t& parsedPosition, Types&&... args)
130 {
131 const auto startOfSpecifier = it;
132 if (it.advanceUntilMatches('}')) // We have an already matched '{' when arriving here
133 {
134 auto specifier = startOfSpecifier.sliceFromStartUntil(it);
135 auto specifierPosition = specifier;
136 if (specifier.advanceUntilMatches(':'))
137 {
138 specifierPosition = startOfSpecifier.sliceFromStartUntil(specifier);
139 (void)specifier.stepForward(); // eat '{'
140 }
141 (void)specifierPosition.stepForward(); // eat '{'
142 (void)it.stepForward(); // eat '}'
143 const StringView positionString = StringView::fromIteratorUntilEnd(specifierPosition);
144 const StringView specifierString = StringView::fromIteratorUntilEnd(specifier);
145 if (not positionString.isEmpty())
146 {
147 if (not positionString.parseInt32(parsedPosition))
148 {
149 return false;
150 }
151 }
152 constexpr auto maxArgs = sizeof...(args);
153 return formatArgument<maxArgs, maxArgs>(data, specifierString, parsedPosition, forward<Types>(args)...);
154 }
155 return false;
156 }
157
158 template <typename... Types>
159 static bool executeFormat(StringFormatOutput& data, RangeIterator it, Types&&... args)
160 {
161 StringCodePoint matchedChar;
162
163 auto start = it;
164 int32_t position = 0;
165 int32_t maxPosition = 0;
166 while (true)
167 {
168 if (it.advanceUntilMatchesAny({'{', '}'}, matchedChar)) // match start or end of specifier
169 {
170 if (it.isFollowedBy(matchedChar))
171 SC_LANGUAGE_UNLIKELY // if it's the same matched, let's escape it
172 {
173 (void)it.stepForward(); // we want to make sure we insert the escaped '{' or '}'
174 if (not data.append(StringView::fromIterators(start, it)))
175 return false;
176 (void)it.stepForward(); // we don't want to insert the additional '{' or '}' needed for escaping
177 start = it;
178 }
179 else if (matchedChar == '{') // it's a '{' not followed by itself, so let's parse specifier
180 {
181 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
182 return false;
183 // try parse '}' and eventually format
184 int32_t parsedPosition = position;
185 if (not parsePosition(data, it, parsedPosition, forward<Types>(args)...))
186 return false;
187 start = it;
188 position += 1;
189 maxPosition = max(maxPosition, parsedPosition + 1);
190 }
191 else
192 {
193 return false; // arriving here means end of string with as single, unescaped '}'
194 }
195 }
196 else
197 {
198 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
199 return false;
200 return maxPosition == static_cast<int32_t>(sizeof...(args)); // check right number of args
201 }
202 }
203 }
204};
205
206template <typename RangeIterator>
207template <typename... Types>
209{
210 SC_TRY(fmt.getEncoding() != StringEncoding::Utf16); // UTF16 format strings are not supported
211 data.onFormatBegin();
212 if (Implementation::executeFormat(data, fmt.getIterator<RangeIterator>(), forward<Types>(args)...))
213 SC_LANGUAGE_LIKELY { return data.onFormatSucceeded(); }
214 else
215 {
216 data.onFormatFailed();
217 return false;
218 }
219}
220
221namespace SC
222{
223// clang-format off
224template <> struct SC_STRINGS_EXPORT StringFormatterFor<float> {static bool format(StringFormatOutput&, const StringSpan, const float);};
225template <> struct SC_STRINGS_EXPORT StringFormatterFor<double> {static bool format(StringFormatOutput&, const StringSpan, const double);};
226#if SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL
227#if SC_PLATFORM_64_BIT == 0
228template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::ssize_t);};
229#endif
230#else
231#if !SC_PLATFORM_LINUX && !(SC_PLATFORM_WINDOWS && SC_PLATFORM_64_BIT)
232template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::size_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::size_t);};
233template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::ssize_t);};
234#endif
235#endif
236template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::int64_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::int64_t);};
237template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::uint64_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::uint64_t);};
238template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::int32_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::int32_t);};
239template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::uint32_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::uint32_t);};
240template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::int16_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::int16_t);};
241template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::uint16_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::uint16_t);};
242template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::int8_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::int8_t);};
243template <> struct SC_STRINGS_EXPORT StringFormatterFor<SC::uint8_t> {static bool format(StringFormatOutput&, const StringSpan, const SC::uint8_t);};
244template <> struct SC_STRINGS_EXPORT StringFormatterFor<char> {static bool format(StringFormatOutput&, const StringSpan, const char);};
245template <> struct SC_STRINGS_EXPORT StringFormatterFor<bool> {static bool format(StringFormatOutput&, const StringSpan, const bool);};
246template <> struct SC_STRINGS_EXPORT StringFormatterFor<StringView> {static bool format(StringFormatOutput&, const StringSpan, const StringView);};
247template <> struct SC_STRINGS_EXPORT StringFormatterFor<const char*> {static bool format(StringFormatOutput&, const StringSpan, const char*);};
248template <> struct SC_STRINGS_EXPORT StringFormatterFor<const void*> {static bool format(StringFormatOutput&, const StringSpan, const void*);};
249#if SC_PLATFORM_WINDOWS
250template <> struct SC_STRINGS_EXPORT StringFormatterFor<wchar_t> {static bool format(StringFormatOutput&, const StringSpan, const wchar_t);};
251template <> struct SC_STRINGS_EXPORT StringFormatterFor<const wchar_t*> {static bool format(StringFormatOutput&, const StringSpan, const wchar_t*);};
252#endif
253template <> struct SC_STRINGS_EXPORT StringFormatterFor<StringSpan> {static bool format(StringFormatOutput&, const StringSpan, const StringSpan);};
254struct StringPath;
255template <> struct SC_STRINGS_EXPORT StringFormatterFor<StringPath> {static bool format(StringFormatOutput&, const StringSpan, const StringPath&);};
256
257// clang-format on
258
259template <int N>
260struct StringFormatterFor<char[N]>
261{
262 static bool format(StringFormatOutput& data, const StringView specifier, const char* str)
263 {
264 const StringView sv({str, N - 1}, true, StringEncoding::Ascii);
265 return StringFormatterFor<StringView>::format(data, specifier, sv);
266 }
267};
268
269} // namespace SC
constexpr const T & max(const T &t1, const T &t2)
Finds the maximum of two values.
Definition Compiler.h:304
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition PrimitiveTypes.h:28
constexpr T && forward(typename TypeTraits::RemoveReference< T >::type &value)
Forwards an lvalue or an rvalue as an rvalue reference.
Definition Compiler.h:279
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition PrimitiveTypes.h:27
decltype(sizeof(0)) size_t
Platform independent unsigned size type.
Definition PrimitiveTypes.h:45
decltype(static_cast< char * >(nullptr) - static_cast< char * >(nullptr)) ssize_t
Platform independent signed size type.
Definition PrimitiveTypes.h:46
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition PrimitiveTypes.h:33
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition PrimitiveTypes.h:29
short int16_t
Platform independent (2) bytes signed int.
Definition PrimitiveTypes.h:36
long long int64_t
Platform independent (8) bytes signed int.
Definition PrimitiveTypes.h:41
signed char int8_t
Platform independent (1) byte signed int.
Definition PrimitiveTypes.h:35
int int32_t
Platform independent (4) bytes signed int.
Definition PrimitiveTypes.h:37
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition Result.h:49
uint32_t StringCodePoint
UTF code point (32 bit)
Definition StringIterator.h:15
Writes to console using SC::StringFormat.
Definition Console.h:26
Definition StringFormat.h:27
void onFormatBegin()
Method to be called when format begins, so that it can be rolled back on failure.
void onFormatFailed()
Method to be called when format fails (will rollback buffer to length before onFormatBegin)
StringFormatOutput(StringEncoding encoding, Console &destination, bool useStdOut)
Constructs a StringFormatOutput object pushing to a console.
bool onFormatSucceeded()
Method to be called when format succeeds.
bool append(StringView text)
Appends the StringView (eventually converting it) to destination buffer.
Formats String with a simple DSL embedded in the format string.
Definition StringFormat.h:84
static bool format(StringFormatOutput &data, StringView fmt, Types &&... args)
Formats fmt StringView using simple DSL where {} are replaced with args.
Definition StringFormat.h:208
An read-only view over a string (to avoid including Strings library when parsing is not needed).
Definition StringSpan.h:37
constexpr StringEncoding getEncoding() const
Get encoding of this StringView.
Definition StringSpan.h:98
Non-owning view over a range of characters with UTF Encoding.
Definition StringView.h:47
constexpr StringIterator getIterator() const
Returns a StringIterator from current StringView.
Definition StringView.h:559
static StringView fromIteratorUntilEnd(StringIterator it, StringEncoding encoding=StringIterator::getEncoding())
Returns a section of a string, from it to end of StringView.
static StringView fromIterators(StringIterator from, StringIterator to, StringEncoding encoding=StringIterator::getEncoding())
Returns a StringView starting at from and ending at to.
EnableIf conditionally defines a type if a boolean template parameter is true.
Definition TypeTraits.h:25