|
- /*!
- * \file utils/print.h
- * \brief
- * A CRTP base class to provide print interface
- *
- * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
- *
- * <dl class=\"section copyright\"><dt>License</dt><dd>
- * 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.
- * </dd></dl>
- */
-
- #ifndef TBX_UTILS_PRINT_H_
- #define TBX_UTILS_PRINT_H_
-
- #include <core/core.h>
- #include <core/crtp.h>
-
- #include <cstring>
- #include <math.h>
- #include <string_view>
- #include <type_traits>
- #include <utility>
- #include <limits>
-
- 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_impl(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 <typename Impl_t, typename Char_t>
- 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<Char_t>;
-
- //! 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 <typename E>
- constexpr typename std::underlying_type_t<E> value(E e) noexcept {
- return static_cast<typename std::underlying_type_t<E>>(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 <typename ...Ts>
- size_t println(Ts&& ...args) {
- size_t r = print (std::forward<Ts>(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 <typename Impl_t, typename Char_t>
- size_t Print<Impl_t, Char_t>::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<long>::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 <typename Impl_t, typename Char_t>
- size_t Print<Impl_t, Char_t>::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<long>::max())
- return print ("ovf");
- if (number < (double)std::numeric_limits<long>::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<digits; ++i)
- rounding /= 10.0;
-
- number += rounding;
-
- // Extract the integer part of the number and print it
- unsigned long int_part = (unsigned long)number;
- double remainder = number - (double)int_part;
- n += print(int_part);
-
- // Print the decimal point, but only if there are digits beyond
- if (digits > 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_ */
|