From 1d11f059184b18243f1ac3515cbf64cb516ee1cd Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 16 Jan 2018 15:11:15 +1100 Subject: [PATCH] cast: move casting functions into util::cast namespace --- alloc/raw/stack.cpp | 2 +- backtrace_execinfo.cpp | 4 +- cast.hpp | 217 +++++++++++++++++++++------------------- cmdopt.cpp | 4 +- exe_linux.cpp | 2 +- hash/sha1.cpp | 40 ++++---- io.cpp | 6 +- io_posix.cpp | 2 +- log.cpp | 2 +- memory/buffer/paged.cpp | 2 +- memory/system.cpp | 2 +- parse.cpp | 2 +- time_posix.cpp | 4 +- 13 files changed, 150 insertions(+), 139 deletions(-) diff --git a/alloc/raw/stack.cpp b/alloc/raw/stack.cpp index d525c906..9e345454 100644 --- a/alloc/raw/stack.cpp +++ b/alloc/raw/stack.cpp @@ -75,7 +75,7 @@ stack::allocate (size_t bytes, size_t alignment) // (from the record struct). allows us to account for alignment. record record; record.as_bytes = ptr - sizeof (record::offset_t); - *record.as_offset = trunc_cast (ptr - m_cursor); + *record.as_offset = util::cast::lossless (ptr - m_cursor); m_cursor = ptr + bytes; return ptr; diff --git a/backtrace_execinfo.cpp b/backtrace_execinfo.cpp index c7d4b2f8..c2343a42 100644 --- a/backtrace_execinfo.cpp +++ b/backtrace_execinfo.cpp @@ -36,7 +36,7 @@ debug::backtrace::backtrace (void): size_t last; size_t size = m_frames.size (); - while ((last = ::backtrace (&m_frames[0], trunc_cast (m_frames.size ()))) == size) + while ((last = ::backtrace (&m_frames[0], util::cast::lossless (m_frames.size ()))) == size) m_frames.resize (size = m_frames.size () * 2); CHECK_GT (last, 0u); @@ -76,7 +76,7 @@ debug::operator <<(std::ostream &os, const debug::backtrace &rhs) { // We don't use the array form of unique_ptr as clang fails on ambigious constructors typedef std::unique_ptr str_t; - str_t names (backtrace_symbols (frames.data (), trunc_cast (frames.size ())), ::free); + str_t names (backtrace_symbols (frames.data (), util::cast::lossless (frames.size ())), ::free); for (unsigned int i = 0; i < frames.size (); ++i) os << frames[i] << '\t' << names.get()[i] << '\t' << addr2line (frames[i]); diff --git a/cast.hpp b/cast.hpp index f477c936..d4806dcb 100644 --- a/cast.hpp +++ b/cast.hpp @@ -23,113 +23,124 @@ #include -/////////////////////////////////////////////////////////////////////////////// -/// Safely cast a numeric type to its (un)signed counterpart, aborting if the -/// dynamically checked result is not representable. May be optimised out if -/// NDEBUG is defined. -/// -/// The signed/unsigned and unsigned/signed cases are split so we can simplify -/// the out of range tests. -/// -/// The same-type case is not provided because we want to error out on -/// unnecessary casts. -template < - typename T, - typename U -> -std::enable_if_t< - sizeof(T) == sizeof(U) && - std::is_unsigned::value && - std::is_signed::value, - T -> -sign_cast (const U u) -{ - CHECK_GE (u, 0); - return static_cast (u); -} - -//----------------------------------------------------------------------------- -template < - typename T, - typename U -> -std::enable_if_t< - sizeof(T) == sizeof (U) && - std::is_signed::value && - std::is_unsigned::value, - T -> -sign_cast (const U u) -{ - CHECK_LT (u, std::numeric_limits::max () / 2); - return static_cast (u); -} - - -///---------------------------------------------------------------------------- -#if !defined(NDEBUG) -/// assert if the value cannot be cast loslessly from U to T, else return the -/// converted value.Note: this is only a debug-time check and is compiled out -/// in optimised builds. -/// -/// relies on equality operators being present and behaving sanely. attempts -/// to preserve NaN values. -/// -/// allows the identity cast (if only to make cross-platform porting easier) -template -constexpr -std::enable_if_t< - std::is_arithmetic::value && - std::is_arithmetic::value, - DstT -> -trunc_cast (const SrcT src) -{ - auto dst = static_cast (src); - - if (!util::is_nan (src)) { - CHECK_EQ (static_cast (dst), src); - } else { - CHECK_NEQ (dst, dst); +namespace util::cast { + /////////////////////////////////////////////////////////////////////////// + /// Safely cast a numeric type to its (un)signed counterpart, aborting if + /// the dynamically checked result is not representable. May be optimised + /// out if NDEBUG is defined. + /// + /// The signed/unsigned and unsigned/signed cases are split so we can + /// simplify the out of range tests. + /// + /// The same-type case is not provided because we want to error out on + /// unnecessary casts. + template < + typename T, + typename U + > + std::enable_if_t< + sizeof(T) == sizeof(U) && + std::is_unsigned::value && + std::is_signed::value, + T + > + sign (const U u) + { + CHECK_GE (u, 0); + return static_cast (u); } - return dst; -} + + //------------------------------------------------------------------------- + template < + typename T, + typename U + > + std::enable_if_t< + sizeof(T) == sizeof (U) && + std::is_signed::value && + std::is_unsigned::value, + T + > + sign (const U u) + { + CHECK_LT (u, std::numeric_limits::max () / 2); + return static_cast (u); + } + + + /////////////////////////////////////////////////////////////////////////// + // cast to a smaller type and check that both values are still equal. + // + // checks will be compiled out if NDEBUG is defined. + template + constexpr NarrowT + narrow (const WideT &val) + { + static_assert (sizeof (NarrowT) <= sizeof (WideT)); + static_assert (std::is_signed_v == std::is_signed_v); + +#ifndef NDEBUG + auto narrow = static_cast (val); + CHECK_EQ (narrow, val); + return narrow; +#else + return static_cast (val); +#endif + } + + + /////////////////////////////////////////////////////////////////////////// + // cast between types checking that equality holds with the result + // + // checks will be compiled out if NDEBUG is defined. + template + constexpr DstT + lossless (const SrcT &src) + { +#ifndef NDEBUG + auto dst = static_cast (src); + + if constexpr (std::is_floating_point_v) { + if (util::is_nan (src)) { + // NaNs must remaing as NaN's. They're important. + CHECK (std::is_floating_point_v); + CHECK (std::isnan (dst)); + } + } + + // Cast dst back to src to check round-trip conversion + // is lossless. + CHECK_EQ (static_cast (dst), src); + return dst; #else + return static_cast (src); +#endif + } -// in release mode we don't give a fuck what happens. cast away all safety. -template -DstT -trunc_cast (const SrcT src) -{ - return static_cast (src); -} - -#endif - - -/////////////////////////////////////////////////////////////////////////////// -/// assert if the value is not a pointer to a subclass of T, else return the -/// converted value. Note: this is only a debug-time check and is compiled out -/// in optimised builds. -template -T* -known_cast (V *v) -{ - CHECK (dynamic_cast (v)); - return static_cast (v); -} - - -//----------------------------------------------------------------------------- -template -T& -known_cast (V &v) -{ - CHECK_NOTHROW (dynamic_cast (v)); - return static_cast (v); -} + + /////////////////////////////////////////////////////////////////////////// + /// assert if the value is not a pointer to a subclass of T, else return + /// the converted value. Note: this is only a debug-time check and is + /// compiled out in optimised builds. + template + T* + known (V *v) + { + CHECK (dynamic_cast (v)); + return static_cast (v); + } + + + //------------------------------------------------------------------------- + template + T& + known (V &v) + { + CHECK_NOTHROW (dynamic_cast (v)); + return static_cast (v); + } +}; #endif diff --git a/cmdopt.cpp b/cmdopt.cpp index 402d8baf..b1d87082 100644 --- a/cmdopt.cpp +++ b/cmdopt.cpp @@ -456,13 +456,13 @@ parser::print_help (const int argc, else std::cout << '\t'; - std::cout << std::setw (trunc_cast (longwidth)); + std::cout << std::setw (util::cast::lossless (longwidth)); if (l != std::cend (m_long)) std::cout << std::get (*l) << '\t'; else std::cout << ' ' << '\t'; - std::cout << std::setw (trunc_cast (longexample)) << ptr->example () << '\t' + std::cout << std::setw (util::cast::lossless (longexample)) << ptr->example () << '\t' << std::setw (0) << std::get (o) << '\n'; } diff --git a/exe_linux.cpp b/exe_linux.cpp index 78e5ff86..a6da675e 100644 --- a/exe_linux.cpp +++ b/exe_linux.cpp @@ -41,7 +41,7 @@ retry: if (written < 0) posix::error::throw_code (); - if (sign_cast (written) == resolved.size ()) { + if (util::cast::sign (written) == resolved.size ()) { resolved.resize (resolved.size () * 2); goto retry; } diff --git a/hash/sha1.cpp b/hash/sha1.cpp index 331056ad..1808b0ee 100644 --- a/hash/sha1.cpp +++ b/hash/sha1.cpp @@ -257,25 +257,25 @@ SHA1::digest (void) const CHECK_EQ (+state, +FINISHED); return { { - trunc_cast ((H[0] >> 24u) & 0xFF), - trunc_cast ((H[0] >> 16u) & 0xFF), - trunc_cast ((H[0] >> 8u) & 0xFF), - trunc_cast ((H[0] ) & 0xFF), - trunc_cast ((H[1] >> 24u) & 0xFF), - trunc_cast ((H[1] >> 16u) & 0xFF), - trunc_cast ((H[1] >> 8u) & 0xFF), - trunc_cast ((H[1] ) & 0xFF), - trunc_cast ((H[2] >> 24u) & 0xFF), - trunc_cast ((H[2] >> 16u) & 0xFF), - trunc_cast ((H[2] >> 8u) & 0xFF), - trunc_cast ((H[2] ) & 0xFF), - trunc_cast ((H[3] >> 24u) & 0xFF), - trunc_cast ((H[3] >> 16u) & 0xFF), - trunc_cast ((H[3] >> 8u) & 0xFF), - trunc_cast ((H[3] ) & 0xFF), - trunc_cast ((H[4] >> 24u) & 0xFF), - trunc_cast ((H[4] >> 16u) & 0xFF), - trunc_cast ((H[4] >> 8u) & 0xFF), - trunc_cast ((H[4] ) & 0xFF) + util::cast::lossless ((H[0] >> 24u) & 0xFF), + util::cast::lossless ((H[0] >> 16u) & 0xFF), + util::cast::lossless ((H[0] >> 8u) & 0xFF), + util::cast::lossless ((H[0] ) & 0xFF), + util::cast::lossless ((H[1] >> 24u) & 0xFF), + util::cast::lossless ((H[1] >> 16u) & 0xFF), + util::cast::lossless ((H[1] >> 8u) & 0xFF), + util::cast::lossless ((H[1] ) & 0xFF), + util::cast::lossless ((H[2] >> 24u) & 0xFF), + util::cast::lossless ((H[2] >> 16u) & 0xFF), + util::cast::lossless ((H[2] >> 8u) & 0xFF), + util::cast::lossless ((H[2] ) & 0xFF), + util::cast::lossless ((H[3] >> 24u) & 0xFF), + util::cast::lossless ((H[3] >> 16u) & 0xFF), + util::cast::lossless ((H[3] >> 8u) & 0xFF), + util::cast::lossless ((H[3] ) & 0xFF), + util::cast::lossless ((H[4] >> 24u) & 0xFF), + util::cast::lossless ((H[4] >> 16u) & 0xFF), + util::cast::lossless ((H[4] >> 8u) & 0xFF), + util::cast::lossless ((H[4] ) & 0xFF) } }; } diff --git a/io.cpp b/io.cpp index a6abb4a1..79783a34 100644 --- a/io.cpp +++ b/io.cpp @@ -142,8 +142,8 @@ util::write (const posix::fd &out, while (remaining) { ssize_t consumed = posix::error::try_value (::write (out, cursor, remaining)); - remaining -= sign_cast (consumed); - cursor += sign_cast (consumed); + remaining -= util::cast::sign (consumed); + cursor += util::cast::sign (consumed); } } @@ -151,7 +151,7 @@ util::write (const posix::fd &out, int indenter::overflow (int ch) { if (m_line_start && ch != '\n') - m_dest->sputn (m_indent.data (), sign_cast (m_indent.size ())); + m_dest->sputn (m_indent.data (), util::cast::sign (m_indent.size ())); m_line_start = ch == '\n'; return m_dest->sputc (ch); diff --git a/io_posix.cpp b/io_posix.cpp index e865c8af..c3b4a46c 100644 --- a/io_posix.cpp +++ b/io_posix.cpp @@ -40,7 +40,7 @@ mapped_file::mapped_file (const ::util::posix::fd &src, int mflags) struct stat meta; ::util::posix::error::try_value (fstat (src, &meta)); - m_size = sign_cast (meta.st_size); + m_size = util::cast::sign (meta.st_size); m_data = (uint8_t *)mmap (NULL, m_size, mflags, MAP_SHARED, src, 0); if (m_data == MAP_FAILED) ::util::posix::error::throw_code (); diff --git a/log.cpp b/log.cpp index a1788dc8..8fbe58e5 100644 --- a/log.cpp +++ b/log.cpp @@ -247,7 +247,7 @@ util::log (util::level_t level, const std::string &msg) std::clog << time_string << " [" << level_colour (level) - << std::setw (trunc_cast (level_width ())) + << std::setw (util::cast::lossless (level_width ())) << std::left << level << std::setw (0) diff --git a/memory/buffer/paged.cpp b/memory/buffer/paged.cpp index 28bc141f..16625a45 100644 --- a/memory/buffer/paged.cpp +++ b/memory/buffer/paged.cpp @@ -127,7 +127,7 @@ paged::release (char *desired) // bail if the region is alread unmapped, or if it's not sufficiently // behind the current cursor. - if (desired >= m_cursor || sign_cast (m_cursor - desired) < m_window) + if (desired >= m_cursor || util::cast::sign (m_cursor - desired) < m_window) return; desired += m_window; diff --git a/memory/system.cpp b/memory/system.cpp index dcd365af..a5596310 100644 --- a/memory/system.cpp +++ b/memory/system.cpp @@ -28,7 +28,7 @@ util::memory::pagesize (void) static size_t val; if (!val) { - val = sign_cast (posix::error::try_value (sysconf (_SC_PAGE_SIZE))); + val = util::cast::sign (posix::error::try_value (sysconf (_SC_PAGE_SIZE))); } return val; diff --git a/parse.cpp b/parse.cpp index 77f08daf..f32bb440 100644 --- a/parse.cpp +++ b/parse.cpp @@ -93,5 +93,5 @@ int util::parse (util::view str) { auto intermediate = util::parse (str); - return trunc_cast (intermediate); + return util::cast::lossless (intermediate); } diff --git a/time_posix.cpp b/time_posix.cpp index 7805fad2..76558508 100644 --- a/time_posix.cpp +++ b/time_posix.cpp @@ -28,8 +28,8 @@ util::sleep (uint64_t ns) { struct timespec req, rem; - req.tv_sec = trunc_cast (ns / SECOND); - req.tv_nsec = trunc_cast (ns % SECOND); + req.tv_sec = util::cast::lossless (ns / SECOND); + req.tv_nsec = util::cast::lossless (ns % SECOND); while (nanosleep (&req, &rem)) { req = rem;