Skip to content

Commit 144fadd

Browse files
committed
Strings: Add Console methods to enable printing to stderr
1 parent 75eaa83 commit 144fadd

File tree

5 files changed

+98
-28
lines changed

5 files changed

+98
-28
lines changed

Libraries/Strings/Console.h

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,63 @@ struct SC_COMPILER_EXPORT Console
2727
/// @param conversionBuffer The optional buffer used for UTF conversions
2828
Console(Span<char> conversionBuffer = {});
2929

30-
/// @brief Prints a formatted string using SC::StringFormat
31-
/// @tparam Types Types of `args`
32-
/// @param fmt Format string
33-
/// @param args Arguments to be formatted in the string
30+
/// @brief Prints a formatted string using SC::StringFormat to stdout
3431
/// @return `true` if message has been printed successfully to Console
3532
template <typename... Types>
3633
bool print(StringSpan fmt, Types&&... args)
3734
{
38-
StringFormatOutput output(fmt.getEncoding(), *this);
39-
if (fmt.getEncoding() == StringEncoding::Ascii || fmt.getEncoding() == StringEncoding::Utf8)
40-
{
41-
// It's ok parsing format string '{' and '}' both for utf8 and ascii with StringIteratorASCII
42-
// because on a valid UTF8 string, these chars are unambiguously recognizable
43-
return StringFormat<StringIteratorASCII>::format(output, fmt, forward<Types>(args)...);
44-
}
45-
return false; // UTF16/32 format strings are not supported
35+
return printInternal(true, fmt, forward<Types>(args)...);
4636
}
4737

48-
/// @brief Prints a StringSpan to console
49-
/// @param str The StringSpan to print
38+
/// @brief Prints a formatted string using SC::StringFormat to stderr
39+
/// @return `true` if message has been printed successfully to Console
40+
template <typename... Types>
41+
bool printError(StringSpan fmt, Types&&... args)
42+
{
43+
return printInternal(false, fmt, forward<Types>(args)...);
44+
}
45+
46+
/// @brief Prints a string to console
5047
void print(const StringSpan str);
5148

52-
/// @brief Prints a StringSpan to console and adds a newline at the end of it
53-
/// @param str The StringSpan to print
54-
void printLine(const StringSpan str);
49+
/// @brief Prints a string to stderr
50+
void printError(const StringSpan str);
5551

56-
/// @brief Flushes the console output buffer
52+
/// @brief Flushes stdout
5753
void flush();
5854

55+
/// @brief Flushes stderr
56+
void flushStdErr();
57+
58+
/// @brief Prints a string to stdout and adds a newline at the end of it
59+
void printLine(const StringSpan str);
60+
61+
/// @brief Prints a string to stderr and adds a newline at the end of it
62+
void printErrorLine(const StringSpan str);
63+
5964
/// @brief Tries attaching current process to parent console (Windows only, has no effect elsewhere)
6065
/// @returns `true` if the parent console has been attached (Windows only, returns true elsewhere)
6166
static bool tryAttachingToParentConsole();
6267

6368
private:
69+
template <typename... Types>
70+
bool printInternal(bool useStdOut, StringSpan fmt, Types&&... args)
71+
{
72+
StringFormatOutput output(fmt.getEncoding(), *this, useStdOut);
73+
if (fmt.getEncoding() == StringEncoding::Ascii || fmt.getEncoding() == StringEncoding::Utf8)
74+
{
75+
// It's ok parsing format string '{' and '}' both for utf8 and ascii with StringIteratorASCII
76+
// because on a valid UTF8 string, these chars are unambiguously recognizable
77+
return StringFormat<StringIteratorASCII>::format(output, fmt, forward<Types>(args)...);
78+
}
79+
return false; // UTF16/32 format strings are not supported
80+
}
6481
Span<char> conversionBuffer;
6582
#if SC_PLATFORM_WINDOWS
66-
void* handle;
83+
void printWindows(const StringSpan str, void* handle);
84+
85+
void* hStdOut;
86+
void* hStdErr;
6787
bool isConsole = true;
6888
bool isDebugger = true;
6989
#endif

Libraries/Strings/Internal/Console.inl

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
SC::Console::Console(Span<char> conversionBuffer) : conversionBuffer(conversionBuffer)
1313
{
1414
#if SC_PLATFORM_WINDOWS
15-
handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
16-
isConsole = ::GetFileType(handle) == FILE_TYPE_CHAR;
15+
hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
16+
hStdErr = ::GetStdHandle(STD_OUTPUT_HANDLE);
17+
isConsole = ::GetFileType(hStdOut) == FILE_TYPE_CHAR;
1718
isDebugger = ::IsDebuggerPresent() == TRUE;
1819
#endif
1920
}
@@ -33,20 +34,55 @@ void SC::Console::printLine(const StringSpan str)
3334
print("\n"_a8);
3435
}
3536

37+
void SC::Console::printErrorLine(const StringSpan str)
38+
{
39+
printError(str);
40+
printError("\n"_a8);
41+
}
42+
3643
void SC::Console::flush()
3744
{
3845
#if SC_PLATFORM_WINDOWS
39-
::FlushFileBuffers(handle);
46+
::FlushFileBuffers(hStdOut);
4047
#else
4148
::fflush(stdout);
4249
#endif
4350
}
4451

52+
void SC::Console::flushStdErr()
53+
{
54+
#if SC_PLATFORM_WINDOWS
55+
::FlushFileBuffers(hStdErr);
56+
#else
57+
::fflush(stderr);
58+
#endif
59+
}
60+
4561
void SC::Console::print(const StringSpan str)
4662
{
4763
if (str.isEmpty())
4864
return;
4965
#if SC_PLATFORM_WINDOWS
66+
printWindows(str, hStdOut);
67+
#else
68+
::fwrite(str.bytesWithoutTerminator(), sizeof(char), str.sizeInBytes(), stdout);
69+
#endif
70+
}
71+
72+
void SC::Console::printError(const StringSpan str)
73+
{
74+
if (str.isEmpty())
75+
return;
76+
#if SC_PLATFORM_WINDOWS
77+
printWindows(str, hStdErr);
78+
#else
79+
::fwrite(str.bytesWithoutTerminator(), sizeof(char), str.sizeInBytes(), stderr);
80+
#endif
81+
}
82+
83+
#if SC_PLATFORM_WINDOWS
84+
void SC::Console::printWindows(const StringSpan str, void* handle)
85+
{
5086
char fallbackBuffer[256];
5187
if (conversionBuffer.sizeInBytes() < 256)
5288
{
@@ -214,7 +250,5 @@ void SC::Console::print(const StringSpan str)
214250
{
215251
conversionBuffer = {};
216252
}
217-
#else
218-
::fwrite(str.bytesWithoutTerminator(), sizeof(char), str.sizeInBytes(), stdout);
219-
#endif
220253
}
254+
#endif

Libraries/Strings/Internal/StringFormat.inl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,14 @@ bool StringFormatOutput::append(StringView text)
193193
}
194194
if (console != nullptr)
195195
{
196-
console->print(text);
196+
if (useStdOut)
197+
{
198+
console->print(text);
199+
}
200+
else
201+
{
202+
console->printError(text);
203+
}
197204
return true;
198205
}
199206
else if (growableBuffer != nullptr)
@@ -207,7 +214,8 @@ bool StringFormatOutput::append(StringView text)
207214
}
208215
}
209216

210-
StringFormatOutput::StringFormatOutput(StringEncoding encoding, Console& newConsole) : encoding(encoding)
217+
StringFormatOutput::StringFormatOutput(StringEncoding encoding, Console& newConsole, bool useStdOut)
218+
: encoding(encoding), useStdOut(useStdOut)
211219
{
212220
console = &newConsole;
213221
}

Libraries/Strings/StringFormat.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ struct SC_COMPILER_EXPORT StringFormatOutput
3030
/// @brief Constructs a StringFormatOutput object pushing to a console
3131
/// @param encoding The given encoding
3232
/// @param destination The destination console
33-
StringFormatOutput(StringEncoding encoding, Console& destination);
33+
/// @param useStdOut If `true` output is sent to stdout, otherwise to stderr
34+
StringFormatOutput(StringEncoding encoding, Console& destination, bool useStdOut);
3435

3536
/// @brief Appends the StringView (eventually converting it) to destination buffer
3637
/// @param text The StringView to be appended to buffer or console
@@ -50,6 +51,7 @@ struct SC_COMPILER_EXPORT StringFormatOutput
5051
private:
5152
IGrowableBuffer* growableBuffer = nullptr;
5253
StringEncoding encoding;
54+
bool useStdOut = true;
5355

5456
Console* console = nullptr;
5557
size_t backupSize = 0;

Tests/Libraries/Strings/ConsoleTest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ struct SC::ConsoleTest : public SC::TestCase
3232
SC_TEST_EXPECT(console.print("test {}", StringSpan("1")));
3333
SC_TEST_EXPECT(not console.print("test {}"_u16, StringSpan("1")));
3434
}
35+
if (test_section("printError"))
36+
{
37+
console.printError("Test Error\n");
38+
console.printErrorLine("Test Error Line");
39+
console.flushStdErr();
40+
}
3541
}
3642
};
3743

0 commit comments

Comments
 (0)