Add string_stream

This commit is contained in:
Madeline Busig 2024-03-25 21:24:28 -06:00
parent 59314b6d95
commit c4f2b74e99

View File

@ -0,0 +1,142 @@
#pragma once
#include "mtl/stream.hpp"
#include "mtl/string.hpp"
namespace mtl {
/**
* \brief Basic string builder
*
* Mimics std::stringstream basic functionality. Does not provide thorough
* formatting options, only decimal/hexadecimal switching. If more extensive
* formatting is needed, use mtl::string_streamx instead.
*
* 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_stream {
istring& m_str;
bool m_hex = false;
public:
string_stream(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_stream& operator<<(const string_view& str) {
m_str.append(str);
return *this;
}
string_stream& operator<<(char ch) {
m_str += ch;
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_stream& operator<<(T x) {
string<16> str;
m_str.append(to_string(x, str, m_hex));
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_stream& operator<<(T x) {
string<16> str;
m_str.append(to_string(x, str, m_hex));
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_stream& operator<<(T x) {
m_str.append(x ? "true" : "false");
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_stream& operator<<(T addr) {
string<16> str;
m_str.append(to_string(addr, str));
return *this;
}
string_stream& operator<<(_flush) {
flush();
return *this;
}
string_stream& operator<<(_endl) {
m_str += '\n';
flush();
return *this;
}
string_stream& operator<<(_dec) {
m_hex = false;
return *this;
}
string_stream& operator<<(_hex) {
m_hex = true;
return *this;
}
};
} // namespace mtl