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{
8template <typename T>
9struct Vector;
10struct Console;
11struct String;
12template <int N>
13struct SmallString;
14template <typename T>
15struct StringFormatterFor;
16
19
22{
27
31 StringFormatOutput(StringEncoding encoding, Console& destination);
32
36 [[nodiscard]] bool append(StringView text);
37
40
43
46 [[nodiscard]] bool onFormatSucceeded();
47
48 private:
49 Vector<char>* data = nullptr;
50 Console* console = nullptr;
51 StringEncoding encoding;
52 size_t backupSize = 0;
53};
54
77template <typename RangeIterator>
79{
86 template <typename... Types>
87 [[nodiscard]] static bool format(StringFormatOutput& data, StringView fmt, Types&&... args);
88
89 private:
90 struct Implementation;
91};
93
94} // namespace SC
95
96//-----------------------------------------------------------------------------------------------------------------------
97// Implementations Details
98//-----------------------------------------------------------------------------------------------------------------------
99template <typename RangeIterator>
100struct SC::StringFormat<RangeIterator>::Implementation
101{
102 template <int Total, int N, typename T, typename... Rest>
103 static bool formatArgument(StringFormatOutput& data, StringView specifier, int position, T&& arg, Rest&&... rest)
104 {
105 if (position == Total - N)
106 {
107 using First = typename TypeTraits::RemoveConst<typename TypeTraits::RemoveReference<T>::type>::type;
108 return StringFormatterFor<First>::format(data, specifier, arg);
109 }
110 else
111 {
112 return formatArgument<Total, N - 1>(data, specifier, position, forward<Rest>(rest)...);
113 }
114 }
115
116 template <int Total, int N, typename... Args>
117 static typename SC::TypeTraits::EnableIf<sizeof...(Args) == 0, bool>::type formatArgument(StringFormatOutput&,
118 StringView, int, Args...)
119 {
120 return false;
121 }
122
123 template <typename... Types>
124 static bool parsePosition(StringFormatOutput& data, RangeIterator& it, int32_t& parsedPosition, Types&&... args)
125 {
126 const auto startOfSpecifier = it;
127 if (it.advanceUntilMatches('}')) // We have an already matched '{' when arriving here
128 {
129 auto specifier = startOfSpecifier.sliceFromStartUntil(it);
130 auto specifierPosition = specifier;
131 if (specifier.advanceUntilMatches(':'))
132 {
133 specifierPosition = startOfSpecifier.sliceFromStartUntil(specifier);
134 (void)specifier.stepForward(); // eat '{'
135 }
136 (void)specifierPosition.stepForward(); // eat '{'
137 (void)it.stepForward(); // eat '}'
138 const StringView positionString = StringView::fromIteratorUntilEnd(specifierPosition);
139 const StringView specifierString = StringView::fromIteratorUntilEnd(specifier);
140 if (not positionString.isEmpty())
141 {
142 if (not positionString.parseInt32(parsedPosition))
143 {
144 return false;
145 }
146 }
147 constexpr auto maxArgs = sizeof...(args);
148 return formatArgument<maxArgs, maxArgs>(data, specifierString, parsedPosition, forward<Types>(args)...);
149 }
150 return false;
151 }
152
153 template <typename... Types>
154 static bool executeFormat(StringFormatOutput& data, RangeIterator it, Types&&... args)
155 {
156 StringCodePoint matchedChar;
157
158 auto start = it;
159 int32_t position = 0;
160 int32_t maxPosition = 0;
161 while (true)
162 {
163 if (it.advanceUntilMatchesAny({'{', '}'}, matchedChar)) // match start or end of specifier
164 {
165 if (it.isFollowedBy(matchedChar))
166 SC_LANGUAGE_UNLIKELY // if it's the same matched, let's escape it
167 {
168 (void)it.stepForward(); // we want to make sure we insert the escaped '{' or '}'
169 if (not data.append(StringView::fromIterators(start, it)))
170 return false;
171 (void)it.stepForward(); // we don't want to insert the additional '{' or '}' needed for escaping
172 start = it;
173 }
174 else if (matchedChar == '{') // it's a '{' not followed by itself, so let's parse specifier
175 {
176 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
177 return false;
178 // try parse '}' and eventually format
179 int32_t parsedPosition = position;
180 if (not parsePosition(data, it, parsedPosition, forward<Types>(args)...))
181 return false;
182 start = it;
183 position += 1;
184 maxPosition = max(maxPosition, parsedPosition + 1);
185 }
186 else
187 {
188 return false; // arriving here means end of string with as single, unescaped '}'
189 }
190 }
191 else
192 {
193 if (not data.append(StringView::fromIterators(start, it))) // write everything before '{
194 return false;
195 return maxPosition == static_cast<int32_t>(sizeof...(args)); // check right number of args
196 }
197 }
198 }
199};
200
201template <typename RangeIterator>
202template <typename... Types>
204{
205 data.onFormatBegin();
206 if (Implementation::executeFormat(data, fmt.getIterator<RangeIterator>(), forward<Types>(args)...))
207 SC_LANGUAGE_LIKELY { return data.onFormatSucceeded(); }
208 else
209 {
210 data.onFormatFailed();
211 return false;
212 }
213}
214
215namespace SC
216{
217// clang-format off
218template <> struct SC_COMPILER_EXPORT StringFormatterFor<float> {static bool format(StringFormatOutput&, const StringView, const float);};
219template <> struct SC_COMPILER_EXPORT StringFormatterFor<double> {static bool format(StringFormatOutput&, const StringView, const double);};
220#if SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL
221#if SC_PLATFORM_64_BIT == 0
222template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringView, const SC::ssize_t);};
223#endif
224#else
225#if !SC_PLATFORM_LINUX
226template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::size_t> {static bool format(StringFormatOutput&, const StringView, const SC::size_t);};
227template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::ssize_t> {static bool format(StringFormatOutput&, const StringView, const SC::ssize_t);};
228#endif
229#endif
230template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int64_t> {static bool format(StringFormatOutput&, const StringView, const SC::int64_t);};
231template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint64_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint64_t);};
232template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int32_t> {static bool format(StringFormatOutput&, const StringView, const SC::int32_t);};
233template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint32_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint32_t);};
234template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int16_t> {static bool format(StringFormatOutput&, const StringView, const SC::int16_t);};
235template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint16_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint16_t);};
236template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::int8_t> {static bool format(StringFormatOutput&, const StringView, const SC::int8_t);};
237template <> struct SC_COMPILER_EXPORT StringFormatterFor<SC::uint8_t> {static bool format(StringFormatOutput&, const StringView, const SC::uint8_t);};
238template <> struct SC_COMPILER_EXPORT StringFormatterFor<char> {static bool format(StringFormatOutput&, const StringView, const char);};
239template <> struct SC_COMPILER_EXPORT StringFormatterFor<bool> {static bool format(StringFormatOutput&, const StringView, const bool);};
240template <> struct SC_COMPILER_EXPORT StringFormatterFor<StringView> {static bool format(StringFormatOutput&, const StringView, const StringView);};
241template <> struct SC_COMPILER_EXPORT StringFormatterFor<String> {static bool format(StringFormatOutput&, const StringView, const String&);};
242template <> struct SC_COMPILER_EXPORT StringFormatterFor<const char*> {static bool format(StringFormatOutput&, const StringView, const char*);};
243template <> struct SC_COMPILER_EXPORT StringFormatterFor<const void*> {static bool format(StringFormatOutput&, const StringView, const void*);};
244#if SC_PLATFORM_WINDOWS
245template <> struct SC_COMPILER_EXPORT StringFormatterFor<wchar_t> {static bool format(StringFormatOutput&, const StringView, const wchar_t);};
246template <> struct SC_COMPILER_EXPORT StringFormatterFor<const wchar_t*> {static bool format(StringFormatOutput&, const StringView, const wchar_t*);};
247#endif
248
249template <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());}};
250// clang-format on
251
252template <int N>
253struct StringFormatterFor<char[N]>
254{
255 static bool format(StringFormatOutput& data, const StringView specifier, const char* str)
256 {
257 const StringView sv({str, N - 1}, true, StringEncoding::Ascii);
258 return StringFormatterFor<StringView>::format(data, specifier, sv);
259 }
260};
261} // 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.
Writes to console using SC::StringFormat.
Definition: Console.h:27
Formats String with a simple DSL embedded in the format string.
Definition: StringFormat.h:79
static bool format(StringFormatOutput &data, StringView fmt, Types &&... args)
Formats fmt StringView using simple DSL where {} are replaced with args.
Definition: StringFormat.h:203
Allows pushing results of StringFormat to a buffer or to the console.
Definition: StringFormat.h:22
void onFormatBegin()
Method to be called when format begins, so that it can be rolled back on failure.
StringFormatOutput(StringEncoding encoding, Vector< char > &destination)
Constructs a StringFormatOutput object pushing to a destination buffer.
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:742
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
A contiguous sequence of heap allocated elements.
Definition: Vector.h:51