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