Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic C++ output streaming support #2551

Merged
merged 10 commits into from
Sep 13, 2022
33 changes: 13 additions & 20 deletions Sming/Core/DateTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,6 @@ String DateTime::format(const char* sFormat)

String sReturn;

// Append a number to the return buffer, padding to a fixed number of digits
auto appendNumber = [&sReturn](unsigned number, unsigned digits, char padChar = '0') {
char buf[8];
ultoa_wp(number, buf, 10, digits, padChar);
sReturn.concat(buf, digits);
};

char c;
while((c = *sFormat++) != '\0') {
if(c != '%') {
Expand All @@ -298,10 +291,10 @@ String DateTime::format(const char* sFormat)
sReturn += Year;
break;
case 'y': // Year, last 2 digits as a decimal number [00..99]
appendNumber(Year % 100, 2);
sReturn.concat(Year % 100, DEC, 2);
break;
case 'C': // Year, first 2 digits as a decimal number [00..99]
appendNumber(Year / 100, 2);
sReturn.concat(Year / 100, DEC, 2);
break;
// Month (not implemented: Om)
case 'b': // Abbreviated month name, e.g. Oct (always English)
Expand All @@ -312,18 +305,18 @@ String DateTime::format(const char* sFormat)
sReturn += CStringArray(flashMonthNames)[Month];
break;
case 'm': // Month as a decimal number [01..12]
appendNumber(Month + 1, 2);
sReturn.concat(Month + 1, DEC, 2);
break;
// Week (not implemented: OU, OW, OV)
case 'U': // Week of the year as a decimal number (Sunday is the first day of the week) [00..53]
appendNumber(calcWeek(0), 2);
sReturn.concat(calcWeek(0), DEC, 2);
break;
case 'V': // ISO 8601 week number (01-53)
// !@todo Calculation of ISO 8601 week number is crude and frankly wrong but does anyone care?
appendNumber(calcWeek(1) + 1, 2);
sReturn.concat(calcWeek(1) + 1, DEC, 2);
break;
case 'W': // Week of the year as a decimal number (Monday is the first day of the week) [00..53]
appendNumber(calcWeek(1), 2);
sReturn.concat(calcWeek(1), DEC, 2);
break;
case 'x': // Locale preferred date format
sReturn += format(_F(LOCALE_DATE));
Expand All @@ -333,13 +326,13 @@ String DateTime::format(const char* sFormat)
break;
// Day of year/month (Not implemented: Od, Oe)
case 'j': // Day of the year as a decimal number [001..366]
appendNumber(DayofYear, 3);
sReturn.concat(DayofYear, DEC, 3);
break;
case 'd': // Day of the month as a decimal number [01..31]
appendNumber(Day, 2);
sReturn.concat(Day, DEC, 2);
break;
case 'e': // Day of the month as a decimal number [ 1,31]
appendNumber(Day, 2, ' ');
sReturn.concat(Day, DEC, 2, ' ');
break;
// Day of week (Not implemented: Ow, Ou)
case 'w': // Weekday as a decimal number with Sunday as 0 [0..6]
Expand All @@ -356,16 +349,16 @@ String DateTime::format(const char* sFormat)
break;
// Time (not implemented: OH, OI, OM, OS)
case 'H': // Hour as a decimal number, 24 hour clock [00..23]
appendNumber(Hour, 2);
sReturn.concat(Hour, DEC, 2);
break;
case 'I': // Hour as a decimal number, 12 hour clock [0..12]
appendNumber(Hour ? ((Hour > 12) ? Hour - 12 : Hour) : 12, 2);
sReturn.concat(Hour ? ((Hour > 12) ? Hour - 12 : Hour) : 12, DEC, 2);
break;
case 'M': // Minute as a decimal number [00..59]
appendNumber(Minute, 2);
sReturn.concat(Minute, DEC, 2);
break;
case 'S': // Second as a decimal number [00..61]
appendNumber(Second, 2);
sReturn.concat(Second, DEC, 2);
break;
// Other (not implemented: Ec, Ex, EX, z, Z)
case 'c': // Locale preferred date and time format, e.g. Tue Dec 11 08:48:32 2018
Expand Down
45 changes: 18 additions & 27 deletions Sming/Wiring/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,6 @@ size_t Print::write(const uint8_t* buffer, size_t size)
return n;
}

size_t Print::print(long num, int base)
{
if(base == 0) {
return write(num);
}

if(base == 10 && num < 0) {
return print('-') + printNumber(static_cast<unsigned long>(-num), base);
}

return printNumber(static_cast<unsigned long>(num), base);
}

// Overload (signed long long)
size_t Print::print(const long long& num, int base)
{
if(base == 10 && num < 0) {
return print('-') + printNumber(static_cast<unsigned long long>(-num), base);
}

return printNumber(static_cast<unsigned long long>(num), base);
}

size_t Print::printf(const char* fmt, ...)
{
size_t buffSize = INITIAL_PRINTF_BUFFSIZE;
Expand All @@ -83,17 +60,31 @@ size_t Print::printf(const char* fmt, ...)
}
}

size_t Print::printNumber(unsigned long num, uint8_t base)
size_t Print::printNumber(unsigned long num, uint8_t base, uint8_t width, char pad)
{
char buf[8 * sizeof(num) + 1]; // Assumes 8-bit chars plus zero byte.
ultoa_wp(num, buf, base, width, pad);
return write(buf);
}

size_t Print::printNumber(const unsigned long long& num, uint8_t base, uint8_t width, char pad)
{
char buf[8 * sizeof(num) + 1]; // Assumes 8-bit chars plus zero byte.
ulltoa_wp(num, buf, base, width, pad);
return write(buf);
}

size_t Print::printNumber(long num, uint8_t base, uint8_t width, char pad)
{
char buf[8 * sizeof(num) + 1]; // Assumes 8-bit chars plus zero byte.
ultoa(num, buf, base);
ltoa_wp(num, buf, base, width, pad);
return write(buf);
}

size_t Print::printNumber(const unsigned long long& num, uint8_t base)
size_t Print::printNumber(const long long& num, uint8_t base, uint8_t width, char pad)
{
char buf[8 * sizeof(num) + 1]; // Assumes 8-bit chars plus zero byte.
ulltoa(num, buf, base);
lltoa_wp(num, buf, base, width, pad);
return write(buf);
}

Expand Down
162 changes: 65 additions & 97 deletions Sming/Wiring/Print.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class Print
*
* @{
*/
size_t print(unsigned long num, int base = DEC)
size_t print(unsigned long num, uint8_t base = DEC)
{
if(base == 0) {
return write(num);
Expand All @@ -124,28 +124,48 @@ class Print
}
}

size_t print(const unsigned long long& num, int base = DEC)
template <typename... Args> size_t print(unsigned long num, Args... args)
{
return printNumber(num, base);
return printNumber(num, args...);
}

size_t print(long, int base = DEC);
template <typename... Args> size_t print(const unsigned long long& num, Args... args)
{
return printNumber(num, args...);
}

size_t print(const long long&, int base = DEC);
size_t print(long num, uint8_t base = DEC)
{
if(base == 0) {
return write(num);
} else {
return printNumber(num, base);
}
}

size_t print(unsigned int num, int base = DEC)
template <typename... Args> size_t print(long num, Args... args)
{
return print((unsigned long)num, base);
return printNumber(num, args...);
}

size_t print(unsigned char num, int base = DEC)
template <typename... Args> size_t print(const long long& num, Args... args)
{
return print((unsigned long)num, base);
return printNumber(num, args...);
}

size_t print(int num, int base = DEC)
template <typename... Args> size_t print(unsigned int num, Args... args)
{
return print((long)num, base);
return print((unsigned long)num, args...);
}

template <typename... Args> size_t print(unsigned char num, Args... args)
{
return print((unsigned long)num, args...);
}

template <typename... Args> size_t print(int num, Args... args)
{
return printNumber((long)num, args...);
}
/** @} */

Expand Down Expand Up @@ -177,101 +197,30 @@ class Print
return write(s.c_str(), s.length());
}

/** @brief Prints a newline to output stream
* @retval size_t Quantity of characters written to stream
*/
size_t println()
{
return print("\r\n");
}

/** @brief Prints a c-string to output stream, appending newline
* @param str c-string to print
* @retval size_t Quantity of characters written to stream
*/
size_t println(const char str[])
{
return print(str) + println();
}

/** @brief Prints a single character to output stream, appending newline
* @param c Character to print
* @retval size_t Quantity of characters written to stream
*/
size_t println(char c)
{
return print(c) + println();
}

/** @name Print an integral number to output stream, appending newline
* @param num Number to print
* @param base The base for output (Default: Decimal (base 10))
* @retval size_t Quantity of characters written to stream
*
* @{
*/
size_t println(unsigned char num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(unsigned int num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(unsigned long num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(const unsigned long long& num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(int num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(long num, int base = DEC)
{
return print(num, base) + println();
}

size_t println(const long long& num, int base = DEC)
{
return print(num, base) + println();
}
/** @} */

/** @brief Print a floating-point number to output stream, appending newline
* @param num Number to print
* @param digits The decimal places to print (Default: 2, e.g. 21.35)
* @retval size_t Quantity of characters written to stream
*/
size_t println(double num, int digits = 2)
/**
* @brief enums can be printed as strings provided they have a `toString(E)` implementation.
*/
template <typename E>
typename std::enable_if<std::is_enum<E>::value && !std::is_convertible<E, int>::value, size_t>::type print(E value)
{
return print(num, digits) + println();
extern String toString(E e);
return print(toString(value));
}

/** @brief Prints a Printable object to output stream, appending newline
* @param p Object to print
/** @brief Prints a newline to output stream
* @retval size_t Quantity of characters written to stream
*/
size_t println(const Printable& p)
size_t println()
{
return print(p) + println();
return print("\r\n");
}

/** @brief Prints a String to output stream, appending newline
* @param s String to print
/** @brief Print value plus newline to output stream
* @retval size_t Quantity of characters written to stream
*/
size_t println(const String& s)
template <typename... Args> size_t println(const Args&... args)
{
return print(s) + println();
return print(args...) + println();
}

/** @brief Prints a formatted c-string to output stream
Expand All @@ -284,8 +233,10 @@ class Print

private:
int write_error = 0;
size_t printNumber(unsigned long num, uint8_t base);
size_t printNumber(const unsigned long long& num, uint8_t base);
size_t printNumber(unsigned long num, uint8_t base = DEC, uint8_t width = 0, char pad = '0');
size_t printNumber(const unsigned long long& num, uint8_t base = DEC, uint8_t width = 0, char pad = '0');
size_t printNumber(long num, uint8_t base = DEC, uint8_t width = 0, char pad = '0');
size_t printNumber(const long long& num, uint8_t base = DEC, uint8_t width = 0, char pad = '0');
size_t printFloat(double num, uint8_t digits);

protected:
Expand All @@ -295,6 +246,23 @@ class Print
}
};

template <typename T> Print& operator<<(Print& p, const T& value)
{
p.print(value);
return p;
}

// Thanks to Arduino forum user Paul V. who suggested this
// clever technique to allow for expressions like
// Serial << "Hello!" << endl;
enum EndLineCode { endl };

inline Print& operator<<(Print& p, EndLineCode)
{
p.println();
return p;
}

/** @} */

#endif // __cplusplus
Loading