Add string_streamx
string_streamx is a string_stream with additional formatting options. Currently the only extra option is the ability to expand strings to a length using a fill character, along with left/right justification. More options similar to std::stringstream may be added in the future. These extra options do come at a performance cost, and string_stream should be preferred unless the extra options are absolutely needed.
This commit is contained in:
parent
c4f2b74e99
commit
ec934592bc
234
include/mtl/string_streamx.hpp
Normal file
234
include/mtl/string_streamx.hpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtl/stream.hpp"
|
||||||
|
#include "mtl/string.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Extended string builder
|
||||||
|
*
|
||||||
|
* Provides the same functionality as mtl::string_stream along with additional
|
||||||
|
* formatting options. Allows expanding strings to a given length using a
|
||||||
|
* fill character. Width is set by piping mtl::setw() and applies to the
|
||||||
|
* next value only. Justification is set by piping mtl::left or mtl::right.
|
||||||
|
*
|
||||||
|
* The extra functionality provided by this class does come at a performance
|
||||||
|
* cost. mtl::string_stream should be preferred unless these options are
|
||||||
|
* absolutely needed.
|
||||||
|
*
|
||||||
|
* Variables are piped to the stream using operator<<. The string is buffered
|
||||||
|
* until flush is called, mtl::flush is piped, or mtl::endl is piped.
|
||||||
|
*/
|
||||||
|
class string_streamx {
|
||||||
|
istring& m_str;
|
||||||
|
struct {
|
||||||
|
size_t width = 0;
|
||||||
|
bool hex = false;
|
||||||
|
bool left = 0;
|
||||||
|
char fill_char = ' ';
|
||||||
|
} m_format;
|
||||||
|
|
||||||
|
public:
|
||||||
|
string_streamx(istring& str) : m_str(str) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Flushes the buffer to the underlying output sequence and
|
||||||
|
* clears the buffer
|
||||||
|
*
|
||||||
|
* By default simply clears the buffer, but can be overriden to
|
||||||
|
* provide more functionality. This function is called internally when
|
||||||
|
* mtl::flush and mtl::endl are piped to the stream. The derived
|
||||||
|
* class must be sure to clear the buffer when finished.
|
||||||
|
*/
|
||||||
|
virtual void flush() {
|
||||||
|
m_str.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
m_str.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
string_streamx& operator<<(const string_view& str) {
|
||||||
|
if (str.length() < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str.append(str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_streamx& operator<<(char ch) {
|
||||||
|
if (1 < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - 1, m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str += ch;
|
||||||
|
|
||||||
|
if (1 < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - 1, m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends the signed integer x
|
||||||
|
*
|
||||||
|
* Writes the integer in decimal if the stream was piped mtl::dec,
|
||||||
|
* hexidecimal if the stream was piped mtl::hex. Defaults to mtl::dec.
|
||||||
|
*
|
||||||
|
* The template does not need to check that the integer is a boolean
|
||||||
|
* because booleans are not signed.
|
||||||
|
*/
|
||||||
|
template <typename T, std::enable_if_t<std::is_signed_v<T>, bool> = true>
|
||||||
|
string_streamx& operator<<(T x) {
|
||||||
|
string<16> str;
|
||||||
|
to_string(x, str, m_format.hex);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str.append(str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends the unsigned integer x
|
||||||
|
*
|
||||||
|
* Writes the integer in decimal if the stream was piped mtl::dec,
|
||||||
|
* hexidecimal if the stream was piped mtl::hex. Defaults to mtl::dec.
|
||||||
|
*
|
||||||
|
* The template must check that the integer is not a boolean because
|
||||||
|
* std::is_unsigned counts booleans as unsigned integers.
|
||||||
|
*/
|
||||||
|
template <typename T, std::enable_if_t<std::is_unsigned_v<T> && !std::is_same_v<T, bool>, bool> = true>
|
||||||
|
string_streamx& operator<<(T x) {
|
||||||
|
string<16> str;
|
||||||
|
to_string(x, str, m_format.hex);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str.append(str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends the boolean x
|
||||||
|
*
|
||||||
|
* Writes the boolean as either "true" or "false".
|
||||||
|
*
|
||||||
|
* This function needs to be templated otherwise non-boolean arguments
|
||||||
|
* might be implicitly converted to boolean. Ex. const char* would be
|
||||||
|
* implicitly converted to boolean instead of mtl::string_view.
|
||||||
|
*/
|
||||||
|
template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
|
||||||
|
string_streamx& operator<<(T x) {
|
||||||
|
string<5> str = x ? "true" : "false";
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str.append(str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends the address of the pointer addr
|
||||||
|
*
|
||||||
|
* Always writes the address is hexidecimal format. Does not pad the
|
||||||
|
* value to the address width.
|
||||||
|
*
|
||||||
|
* The template must check that the pointer is not a character pointer
|
||||||
|
* otherwise C strings will not be converted to a mtl::string_view and
|
||||||
|
* the address of the string will be printed instead.
|
||||||
|
*/
|
||||||
|
template <typename T, std::enable_if_t<std::is_pointer_v<T>
|
||||||
|
&& !std::is_same_v<T, char*>
|
||||||
|
&& !std::is_same_v<T, const char*>, bool> = true>
|
||||||
|
string_streamx& operator<<(T addr) {
|
||||||
|
string<16> str;
|
||||||
|
to_string(addr, str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && !m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_str.append(str);
|
||||||
|
|
||||||
|
if (str.length() < m_format.width && m_format.left) {
|
||||||
|
m_str.append(m_format.width - str.length(), m_format.fill_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_format.width = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_streamx& operator<<(_flush) {
|
||||||
|
flush();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
string_streamx& operator<<(_endl) {
|
||||||
|
m_str += '\n';
|
||||||
|
flush();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_streamx& operator<<(_dec) {
|
||||||
|
m_format.hex = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
string_streamx& operator<<(_hex) {
|
||||||
|
m_format.hex = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_streamx& operator<<(_left) {
|
||||||
|
m_format.left = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
string_streamx& operator<<(_right) {
|
||||||
|
m_format.left = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
string_streamx& operator<<(setw w) {
|
||||||
|
m_format.width = w.m_w;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
Loading…
x
Reference in New Issue
Block a user