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