Sane C++ Libraries
C++ Platform Abstraction Libraries
StringFormat.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Strings/StringView.h"
5
6namespace SC
7{
8struct Console;
9struct String;
10template <int N>
11struct SmallString;
12template <typename T>
13struct StringFormatterFor;
14
17
20{
24 StringFormatOutput(StringEncoding encoding, Buffer& destination);
25
29 StringFormatOutput(StringEncoding encoding, Console& destination);
30
34 [[nodiscard]] bool append(StringView text);
35
38
41
44 [[nodiscard]] bool onFormatSucceeded();
45
46 private:
47 Buffer* data = nullptr;
48 Console* console = nullptr;
49 StringEncoding encoding;
50 size_t backupSize = 0;
51};
52
75template <typename RangeIterator>
77{
84 template <typename... Types>
85 [[nodiscard]] static bool format(StringFormatOutput& data, StringView fmt, Types&&... args);
86
87 private:
88 struct Implementation;
89};
91
92} // namespace SC
93
94//-----------------------------------------------------------------------------------------------------------------------
95// Implementations Details
96//-----------------------------------------------------------------------------------------------------------------------
97template <typename RangeIterator>
98struct SC::StringFormat<RangeIterator>::Implementation
99{
100 template <int Total, int N, typename T, typename... Rest>
101 static bool formatArgument(StringFormatOutput& data, StringView specifier, int position, T&& arg, Rest&&... rest)
102 {
103 if (position == Total - N)
104 {
105 using First = typename TypeTraits::RemoveConst<typename TypeTraits::RemoveReference<T>::type>::type;
106 return StringFormatterFor<First>::format(data, specifier, arg);
107 }
108 else
109 {
110 return formatArgument<Total, N - 1>(data, specifier, position, forward<Rest>(rest)...);
111 }
112 }
113
114 template <int Total, int N, typename... Args>
115 static typename SC::TypeTraits::EnableIf<sizeof...(Args) == 0, bool>::type formatArgument(StringFormatOutput&,
116 StringView, int, Args...)
117 {
118 return false;
119 }
120
121 template <typename... Types>
122 static bool parsePosition(StringFormatOutput& data, RangeIterator& it, int32_t& parsedPosition, Types&&... args)
123 {
124 const auto startOfSpecifier = it;
125 if (it.advanceUntilMatches('}')) // We have an already matched '{' when arriving here
126 {
127 auto specifier = startOfSpecifier.sliceFromStartUntil(it);
128 auto specifierPosition = specifier;
129 if (specifier.advanceUntilMatches(':'))
130 {
131 specifierPosition = startOfSpecifier.sliceFromStartUntil(specifier);
132 (void)specifier.stepForward(); // eat '{'
133 }
134 (void)specifierPosition.stepForward(); // eat '{'
135 (void)it.stepForward(); // eat '}'
136 const StringView positionString = StringView::fromIteratorUntilEnd(specifierPosition);
137 const StringView specifierString = StringView::fromIteratorUntilEnd(specifier);
138 if (not positionString.isEmpty())
139 {
140 if (not positionString.parseInt32(parsedPosition))
141 {
142 return false;
143 }
144 }
145 constexpr auto maxArgs = sizeof...(args);
146 return formatArgument<maxArgs, maxArgs>(data, specifierString, parsedPosition, forward<Types>(args)...);
147 }
148 return false;
149 }
150
151 template <typename... Types>
152 static bool executeFormat(StringFormatOutput& data, RangeIterator it, Types&&... args)
153 {
154 StringCodePoint matchedChar;
155
156 auto start = it;
157 int32_t position = 0;
158 int32_t maxPosition = 0;
159 while (true)
160 {
161 if (it.advanceUntilMatchesAny({'{', '}'}, matchedChar)) // match start or end of specifier
162 {
163 if (it.isFollowedBy(matchedChar))
164 SC_LANGUAGE_UNLIKELY // if it's the same matched, let's escape it
165 {
166 (void)it.stepForward(); // we want to make sure we insert the escaped '{' or '}'
167 if (not data.append(StringView::fromIterators(start, it)))
168 return false;
169 (void)it.stepForward(); // we don't want to insert the additional '{' or '}' needed for escaping
170 start = it;
171 }
172 else if (matchedChar == '{') // it's a '{' not followed by itself, so let's parse specifier
173 {
174 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
175 return false;
176 // try parse '}' and eventually format
177 int32_t parsedPosition = position;
178 if (not parsePosition(data, it, parsedPosition, forward<Types>(args)...))
179 return false;
180 start = it;
181 position += 1;
182 maxPosition = max(maxPosition, parsedPosition + 1);
183 }
184 else
185 {
186 return false; // arriving here means end of string with as single, unescaped '}'
187 }
188 }
189 else
190 {
191 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
192 return false;
193 return maxPosition == static_cast<int32_t>(sizeof...(args)); // check right number of args
194 }
195 }
196 }
197};
198
199template <typename RangeIterator>
200template <typename... Types>
202{
203 data.onFormatBegin();
204 if (Implementation::executeFormat(data, fmt.getIterator<RangeIterator>(), forward<Types>(args)...))
205 SC_LANGUAGE_LIKELY { return data.onFormatSucceeded(); }
206 else
207 {
208 data.onFormatFailed();
209 return false;
210 }
211}
212
213namespace SC
214{
215// clang-format off
216template <> struct SC_COMPILER_EXPORT StringFormatterFor<float> {static bool format(StringFormatOutput&, const StringView, const float);};
217template <> struct SC_COMPILER_EXPORT StringFormatterFor<double> {static bool format(StringFormatOutput&, const StringView, const double);};
218#if SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL
219#if SC_PLATFORM_64_BIT == 0
220template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringView, const SC::ssize_t);};
221#endif
222#else
223#if !SC_PLATFORM_LINUX
224template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::size_t> {static bool format(StringFormatOutput&, const StringView, const SC::size_t);};
225template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringView, const SC::ssize_t);};
226#endif
227#endif
228template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int64_t> {static bool format(StringFormatOutput&, const StringView, const SC::int64_t);};
229template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint64_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint64_t);};
230template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int32_t> {static bool format(StringFormatOutput&, const StringView, const SC::int32_t);};
231template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint32_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint32_t);};
232template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int16_t> {static bool format(StringFormatOutput&, const StringView, const SC::int16_t);};
233template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint16_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint16_t);};
234template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int8_t> {static bool format(StringFormatOutput&, const StringView, const SC::int8_t);};
235template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint8_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint8_t);};
236template <> struct SC_COMPILER_EXPORT StringFormatterFor<char> {static bool format(StringFormatOutput&, const StringView, const char);};
237template <> struct SC_COMPILER_EXPORT StringFormatterFor<bool> {static bool format(StringFormatOutput&, const StringView, const bool);};
238template <> struct SC_COMPILER_EXPORT StringFormatterFor<StringView> {static bool format(StringFormatOutput&, const StringView, const StringView);};
239template <> struct SC_COMPILER_EXPORT StringFormatterFor<String> {static bool format(StringFormatOutput&, const StringView, const String&);};
240template <> struct SC_COMPILER_EXPORT StringFormatterFor<const char*> {static bool format(StringFormatOutput&, const StringView, const char*);};
241template <> struct SC_COMPILER_EXPORT StringFormatterFor<const void*> {static bool format(StringFormatOutput&, const StringView, const void*);};
242#if SC_PLATFORM_WINDOWS
243template <> struct SC_COMPILER_EXPORT StringFormatterFor<wchar_t> {static bool format(StringFormatOutput&, const StringView, const wchar_t);};
244template <> struct SC_COMPILER_EXPORT StringFormatterFor<const wchar_t*> {static bool format(StringFormatOutput&, const StringView, const wchar_t*);};
245#endif
246
247template <int N> struct SC_COMPILER_EXPORT StringFormatterFor<SmallString<N>> {static bool format(StringFormatOutput& sfo, const StringView sv, const SmallString<N>& s){return StringFormatterFor<StringView>::format(sfo,sv,s.view());}};
248// clang-format on
249
250template <int N>
251struct StringFormatterFor<char[N]>
252{
253 static bool format(StringFormatOutput& data, const StringView specifier, const char* str)
254 {
255 const StringView sv({str, N - 1}, true, StringEncoding::Ascii);
256 return StringFormatterFor<StringView>::format(data, specifier, sv);
257 }
258};
259} // namespace SC
constexpr const T & max(const T &t1, const T &t2)
Finds the maximum of two values.
Definition: Compiler.h:302
#define SC_COMPILER_EXPORT
Macro for symbol visibility in non-MSVC compilers.
Definition: Compiler.h:78
int int32_t
Platform independent (4) bytes signed int.
Definition: PrimitiveTypes.h:46
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition: PrimitiveTypes.h:36
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition: PrimitiveTypes.h:42
signed char int8_t
Platform independent (1) byte signed int.
Definition: PrimitiveTypes.h:44
long long int64_t
Platform independent (8) bytes signed int.
Definition: PrimitiveTypes.h:50
unsigned long size_t
Platform independent unsigned size type.
Definition: PrimitiveTypes.h:56
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
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition: PrimitiveTypes.h:37
signed long ssize_t
Platform independent signed size type.
Definition: PrimitiveTypes.h:57
uint32_t StringCodePoint
UTF code point (32 bit)
Definition: StringIterator.h:13
StringEncoding
String Encoding (Ascii, Utf8, Utf16)
Definition: StringIterator.h:17
@ Ascii
Encoding is ASCII.
An heap allocated byte buffer that can optionally use an inline buffer.
Definition: Buffer.h:25
Writes to console using SC::StringFormat.
Definition: Console.h:27
Formats String with a simple DSL embedded in the format string.
Definition: StringFormat.h:77
static bool format(StringFormatOutput &data, StringView fmt, Types &&... args)
Formats fmt StringView using simple DSL where {} are replaced with args.
Definition: StringFormat.h:201
Allows pushing results of StringFormat to a buffer or to the console.
Definition: StringFormat.h:20
StringFormatOutput(StringEncoding encoding, Buffer &destination)
Constructs a StringFormatOutput object pushing to a destination buffer.
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)
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.
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:743
static StringView fromIterators(StringIterator from, StringIterator to)
Returns a StringView from two iterators. The from iterator will be shortened until the start of to.
static StringView fromIteratorUntilEnd(StringIterator it)
Returns a section of a string, from it to end of StringView.
EnableIf conditionally defines a type if a boolean template parameter is true.
Definition: TypeTraits.h:25