From c4f2b74e999758b4a95f7f16c2c05c0af8b69de1 Mon Sep 17 00:00:00 2001 From: Madeline Busig Date: Mon, 25 Mar 2024 21:24:28 -0600 Subject: [PATCH] Add string_stream --- include/mtl/string_stream.hpp | 142 ++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 include/mtl/string_stream.hpp diff --git a/include/mtl/string_stream.hpp b/include/mtl/string_stream.hpp new file mode 100644 index 0000000..0b29b3d --- /dev/null +++ b/include/mtl/string_stream.hpp @@ -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 , 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 && !std::is_same_v, 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 , 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 + && !std::is_same_v + && !std::is_same_v, 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