/*! * \file utils/print.h * \brief * A CRTP base class to provide print interface * * \copyright Copyright (C) 2021 Christos Choutouridis * *
License
* The MIT License (MIT) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *
*/ #ifndef TBX_UTILS_PRINT_H_ #define TBX_UTILS_PRINT_H_ #include #include #include #include #include #include #include #include namespace tbx { /*! * \class Print * \brief * A CRTP print interface * * Requirements: * - size_t write_impl(const Char_t* buffer, size_t size) : Return the number of \c Char_t written * - size_t write_(const Char_t ch) : Return the number of \c Char_t written (normally one). * * \tparam Impl_t The derived type * \tparam Char_t The char type to use */ template class Print { _CRTP_IMPL(Impl_t); public: using value_type = Char_t; using pointer_type = Char_t*; using iterator_type = Char_t*; using const_iterator_type = const Char_t*; using difference_type = std::ptrdiff_t; using size_type = size_t; using str_view_t = std::basic_string_view; //! Enumerator for number base formating enum class Base { BIN =2, OCT =8, DEC =10, HEX =16 }; private: //! \name CRTP requirements //! @{ size_t write_(const Char_t* buffer, size_t size) { return impl().write_impl(buffer, size); } size_t write_(const Char_t ch) { return impl().write_impl(ch); } //! @} protected: Print() noexcept = default; //!< Construct from derived only private: //! Helper tool to convert strong enums to their underlying type template constexpr typename std::underlying_type_t value(E e) noexcept { return static_cast>(e); } size_t print_unsigned(unsigned long n, Base base); // integer conversion base tool size_t print_double(double number, uint8_t digits); // double conversion base tool public: /*! * \brief * Prints a string view * \param str The string view to print * \return The number of printed \c Char_t */ size_t print(const str_view_t str) { return write_(str.data(), str.size()); } /*! * \brief * Prints a string * \param str Pointer to string to print * \return The number of printed \c Char_t */ size_t print(const Char_t* str) { if (str == nullptr) return 0; return write_(str, std::strlen(str)); } /*! * \brief * Prints a buffer of size \c size. If there is a null termination * before the end of the buffer, prints up to termination. * * \param str Pointer to string buffer to print * \param size The size of buffer * \return The number of printed \c Char_t */ size_t print(const Char_t* str, size_t size) { if (str == nullptr) return 0; return write_(str, size); } /*! * \brief * Prints a \c Char_t * \param ch The Char_t to print * \return The number of printed \c Char_t */ size_t print(Char_t ch) { return write_ (ch); } /*! * \brief * Convert and print a long. * \param n The number to print * \param base The number base to use for conversion. * \return The number of printed \c Char_t */ size_t print(long n, Base base =Base::DEC) { size_t cnt =0; if (n < 0) { n = -n; cnt = write_ ('-'); } return cnt + print_unsigned((unsigned long)n, base); } /*! * \brief * Convert and print an int. * \param n The number to print * \param base The number base to use for conversion. * \return The number of printed \c Char_t */ size_t print(int n, Base base= Base::DEC) { return print ((long)n, base); } /*! * \brief * Convert and print an unsigned long. * \param n The number to print * \param base The number base to use for conversion. * \return The number of printed \c Char_t */ size_t print(unsigned long n, Base base= Base::DEC) { return print_unsigned ((unsigned long)n, base); } /*! * \brief * Convert and print an unsigned int. * \param n The number to print * \param base The number base to use for conversion. * \return The number of printed \c Char_t */ size_t print(unsigned int n, Base base= Base::DEC) { return print_unsigned ((unsigned long)n, base); } /*! * \brief * Convert and print adouble * \param n The number to print * \param digits The number of decimal digits to print * \return The number of printed \c Char_t */ size_t print(double n, uint8_t digits = 2) { return print_double (n, digits); } /*! * \brief * Perfect forwarder to print functionality with a new line termination * \tparam Ts The types of parameters * \param args The arguments to pass * \return The number of printed \c Char_t */ template size_t println(Ts&& ...args) { size_t r = print (std::forward(args)...); r += write_ ('\n'); return r; } }; /*! * \brief * Converts and prints an unsigned long * * \tparam Impl_t The derived type * \tparam Char_t The char type to use * * \param n The number to print * \param base The number base to use * \return The number of printed \c Char_t */ template size_t Print::print_unsigned(unsigned long n, Base base) { Char_t buf[8 *sizeof(Char_t) * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. Char_t *str = &buf[sizeof(buf) - 1]; *str = '\0'; do { Char_t c = n % value(base); n /= value(base); *--str = c < 10 ? c + '0' : c + 'A' - 10; } while(n); return write_(str, std::strlen(str)); } /*! * \brief * Converts and prints a double * * \note * Internally, this implementation uses a long to store the integer part of the number. * Thus overflows for numbers bigger than std::numeric_limits::max() / min(). * For these numbers it prints "ovf" instead. * * \tparam Impl_t The derived type * \tparam Char_t The char type to use * * \param n The number to print * \param digits The number of decimal digits to print * \return The number of printed \c Char_t */ template size_t Print::print_double(double number, uint8_t digits) { size_t n = 0; if (std::isnan(number)) return print("nan"); if (std::isinf(number)) return print("inf"); if (number > (double)std::numeric_limits::max()) return print ("ovf"); if (number < (double)std::numeric_limits::min()) return print ("-ovf"); // Handle negative numbers if (number < 0.0) { n += write_ ('-'); number = -number; } // Round correctly so that print(1.999, 2) prints as "2.00" double rounding = 0.5; for (uint8_t i=0; i 0) { n += print('.'); } // Extract digits from the remainder one at a time while (digits-- > 0) { remainder *= 10.0; unsigned int toPrint = (unsigned int)(remainder); n += print(toPrint); remainder -= toPrint; } return n; } } #endif /* TBX_UTILS_PRINT_H_ */