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