cast: move casting functions into util::cast namespace

This commit is contained in:
Danny Robson 2018-01-16 15:11:15 +11:00
parent c6d025453a
commit 1d11f05918
13 changed files with 150 additions and 139 deletions

View File

@ -75,7 +75,7 @@ stack::allocate (size_t bytes, size_t alignment)
// (from the record struct). allows us to account for alignment. // (from the record struct). allows us to account for alignment.
record record; record record;
record.as_bytes = ptr - sizeof (record::offset_t); record.as_bytes = ptr - sizeof (record::offset_t);
*record.as_offset = trunc_cast<uint32_t> (ptr - m_cursor); *record.as_offset = util::cast::lossless <uint32_t> (ptr - m_cursor);
m_cursor = ptr + bytes; m_cursor = ptr + bytes;
return ptr; return ptr;

View File

@ -36,7 +36,7 @@ debug::backtrace::backtrace (void):
size_t last; size_t last;
size_t size = m_frames.size (); size_t size = m_frames.size ();
while ((last = ::backtrace (&m_frames[0], trunc_cast<int> (m_frames.size ()))) == size) while ((last = ::backtrace (&m_frames[0], util::cast::lossless <int> (m_frames.size ()))) == size)
m_frames.resize (size = m_frames.size () * 2); m_frames.resize (size = m_frames.size () * 2);
CHECK_GT (last, 0u); 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 // We don't use the array form of unique_ptr as clang fails on ambigious constructors
typedef std::unique_ptr<char *, decltype(&std::free)> str_t; typedef std::unique_ptr<char *, decltype(&std::free)> str_t;
str_t names (backtrace_symbols (frames.data (), trunc_cast<int> (frames.size ())), ::free); str_t names (backtrace_symbols (frames.data (), util::cast::lossless <int> (frames.size ())), ::free);
for (unsigned int i = 0; i < frames.size (); ++i) for (unsigned int i = 0; i < frames.size (); ++i)
os << frames[i] << '\t' << names.get()[i] << '\t' << addr2line (frames[i]); os << frames[i] << '\t' << names.get()[i] << '\t' << addr2line (frames[i]);

217
cast.hpp
View File

@ -23,113 +23,124 @@
#include <limits> #include <limits>
/////////////////////////////////////////////////////////////////////////////// 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 /// Safely cast a numeric type to its (un)signed counterpart, aborting if
/// NDEBUG is defined. /// 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 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. /// The same-type case is not provided because we want to error out on
template < /// unnecessary casts.
typename T, template <
typename U typename T,
> typename U
std::enable_if_t< >
sizeof(T) == sizeof(U) && std::enable_if_t<
std::is_unsigned<T>::value && sizeof(T) == sizeof(U) &&
std::is_signed<U>::value, std::is_unsigned<T>::value &&
T std::is_signed<U>::value,
> T
sign_cast (const U u) >
{ sign (const U u)
CHECK_GE (u, 0); {
return static_cast<T> (u); CHECK_GE (u, 0);
} return static_cast<T> (u);
//-----------------------------------------------------------------------------
template <
typename T,
typename U
>
std::enable_if_t<
sizeof(T) == sizeof (U) &&
std::is_signed<T>::value &&
std::is_unsigned<U>::value,
T
>
sign_cast (const U u)
{
CHECK_LT (u, std::numeric_limits<U>::max () / 2);
return static_cast<T> (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 <typename DstT, typename SrcT>
constexpr
std::enable_if_t<
std::is_arithmetic<SrcT>::value &&
std::is_arithmetic<DstT>::value,
DstT
>
trunc_cast (const SrcT src)
{
auto dst = static_cast<DstT> (src);
if (!util::is_nan (src)) {
CHECK_EQ (static_cast<SrcT> (dst), src);
} else {
CHECK_NEQ (dst, dst);
} }
return dst;
} //-------------------------------------------------------------------------
template <
typename T,
typename U
>
std::enable_if_t<
sizeof(T) == sizeof (U) &&
std::is_signed<T>::value &&
std::is_unsigned<U>::value,
T
>
sign (const U u)
{
CHECK_LT (u, std::numeric_limits<U>::max () / 2);
return static_cast<T> (u);
}
///////////////////////////////////////////////////////////////////////////
// cast to a smaller type and check that both values are still equal.
//
// checks will be compiled out if NDEBUG is defined.
template <typename NarrowT, typename WideT>
constexpr NarrowT
narrow (const WideT &val)
{
static_assert (sizeof (NarrowT) <= sizeof (WideT));
static_assert (std::is_signed_v<NarrowT> == std::is_signed_v<WideT>);
#ifndef NDEBUG
auto narrow = static_cast<NarrowT> (val);
CHECK_EQ (narrow, val);
return narrow;
#else
return static_cast<NarrowT> (val);
#endif
}
///////////////////////////////////////////////////////////////////////////
// cast between types checking that equality holds with the result
//
// checks will be compiled out if NDEBUG is defined.
template <typename DstT, typename SrcT>
constexpr DstT
lossless (const SrcT &src)
{
#ifndef NDEBUG
auto dst = static_cast<DstT> (src);
if constexpr (std::is_floating_point_v<SrcT>) {
if (util::is_nan (src)) {
// NaNs must remaing as NaN's. They're important.
CHECK (std::is_floating_point_v<DstT>);
CHECK (std::isnan (dst));
}
}
// Cast dst back to src to check round-trip conversion
// is lossless.
CHECK_EQ (static_cast<SrcT> (dst), src);
return dst;
#else #else
return static_cast<DstT> (src);
#endif
}
// in release mode we don't give a fuck what happens. cast away all safety.
template <typename DstT, typename SrcT> ///////////////////////////////////////////////////////////////////////////
DstT /// assert if the value is not a pointer to a subclass of T, else return
trunc_cast (const SrcT src) /// the converted value. Note: this is only a debug-time check and is
{ /// compiled out in optimised builds.
return static_cast<DstT> (src); template <typename T, typename V>
} T*
known (V *v)
#endif {
CHECK (dynamic_cast<T*> (v));
return static_cast<T*> (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 <typename T, typename V> template <typename T, typename V>
T* T&
known_cast (V *v) known (V &v)
{ {
CHECK (dynamic_cast<T*> (v)); CHECK_NOTHROW (dynamic_cast<T> (v));
return static_cast<T*> (v); return static_cast<T> (v);
} }
};
//-----------------------------------------------------------------------------
template <typename T, typename V>
T&
known_cast (V &v)
{
CHECK_NOTHROW (dynamic_cast<T> (v));
return static_cast<T> (v);
}
#endif #endif

View File

@ -456,13 +456,13 @@ parser::print_help (const int argc,
else else
std::cout << '\t'; std::cout << '\t';
std::cout << std::setw (trunc_cast<int> (longwidth)); std::cout << std::setw (util::cast::lossless<int> (longwidth));
if (l != std::cend (m_long)) if (l != std::cend (m_long))
std::cout << std::get<std::string> (*l) << '\t'; std::cout << std::get<std::string> (*l) << '\t';
else else
std::cout << ' ' << '\t'; std::cout << ' ' << '\t';
std::cout << std::setw (trunc_cast<int> (longexample)) << ptr->example () << '\t' std::cout << std::setw (util::cast::lossless<int> (longexample)) << ptr->example () << '\t'
<< std::setw (0) << std::get<std::string> (o) << std::setw (0) << std::get<std::string> (o)
<< '\n'; << '\n';
} }

View File

@ -41,7 +41,7 @@ retry:
if (written < 0) if (written < 0)
posix::error::throw_code (); posix::error::throw_code ();
if (sign_cast<size_t> (written) == resolved.size ()) { if (util::cast::sign <size_t> (written) == resolved.size ()) {
resolved.resize (resolved.size () * 2); resolved.resize (resolved.size () * 2);
goto retry; goto retry;
} }

View File

@ -257,25 +257,25 @@ SHA1::digest (void) const
CHECK_EQ (+state, +FINISHED); CHECK_EQ (+state, +FINISHED);
return { { return { {
trunc_cast<uint8_t> ((H[0] >> 24u) & 0xFF), util::cast::lossless<uint8_t> ((H[0] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 16u) & 0xFF), util::cast::lossless<uint8_t> ((H[0] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 8u) & 0xFF), util::cast::lossless<uint8_t> ((H[0] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[0] ) & 0xFF), util::cast::lossless<uint8_t> ((H[0] ) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 24u) & 0xFF), util::cast::lossless<uint8_t> ((H[1] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 16u) & 0xFF), util::cast::lossless<uint8_t> ((H[1] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 8u) & 0xFF), util::cast::lossless<uint8_t> ((H[1] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[1] ) & 0xFF), util::cast::lossless<uint8_t> ((H[1] ) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 24u) & 0xFF), util::cast::lossless<uint8_t> ((H[2] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 16u) & 0xFF), util::cast::lossless<uint8_t> ((H[2] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 8u) & 0xFF), util::cast::lossless<uint8_t> ((H[2] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[2] ) & 0xFF), util::cast::lossless<uint8_t> ((H[2] ) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 24u) & 0xFF), util::cast::lossless<uint8_t> ((H[3] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 16u) & 0xFF), util::cast::lossless<uint8_t> ((H[3] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 8u) & 0xFF), util::cast::lossless<uint8_t> ((H[3] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[3] ) & 0xFF), util::cast::lossless<uint8_t> ((H[3] ) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 24u) & 0xFF), util::cast::lossless<uint8_t> ((H[4] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 16u) & 0xFF), util::cast::lossless<uint8_t> ((H[4] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 8u) & 0xFF), util::cast::lossless<uint8_t> ((H[4] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[4] ) & 0xFF) util::cast::lossless<uint8_t> ((H[4] ) & 0xFF)
} }; } };
} }

6
io.cpp
View File

@ -142,8 +142,8 @@ util::write (const posix::fd &out,
while (remaining) { while (remaining) {
ssize_t consumed = posix::error::try_value (::write (out, cursor, remaining)); ssize_t consumed = posix::error::try_value (::write (out, cursor, remaining));
remaining -= sign_cast<size_t> (consumed); remaining -= util::cast::sign<size_t> (consumed);
cursor += sign_cast<size_t> (consumed); cursor += util::cast::sign<size_t> (consumed);
} }
} }
@ -151,7 +151,7 @@ util::write (const posix::fd &out,
int int
indenter::overflow (int ch) { indenter::overflow (int ch) {
if (m_line_start && ch != '\n') if (m_line_start && ch != '\n')
m_dest->sputn (m_indent.data (), sign_cast<std::streamsize> (m_indent.size ())); m_dest->sputn (m_indent.data (), util::cast::sign<std::streamsize> (m_indent.size ()));
m_line_start = ch == '\n'; m_line_start = ch == '\n';
return m_dest->sputc (ch); return m_dest->sputc (ch);

View File

@ -40,7 +40,7 @@ mapped_file::mapped_file (const ::util::posix::fd &src, int mflags)
struct stat meta; struct stat meta;
::util::posix::error::try_value (fstat (src, &meta)); ::util::posix::error::try_value (fstat (src, &meta));
m_size = sign_cast<size_t> (meta.st_size); m_size = util::cast::sign<size_t> (meta.st_size);
m_data = (uint8_t *)mmap (NULL, m_size, mflags, MAP_SHARED, src, 0); m_data = (uint8_t *)mmap (NULL, m_size, mflags, MAP_SHARED, src, 0);
if (m_data == MAP_FAILED) if (m_data == MAP_FAILED)
::util::posix::error::throw_code (); ::util::posix::error::throw_code ();

View File

@ -247,7 +247,7 @@ util::log (util::level_t level, const std::string &msg)
std::clog << time_string << " [" std::clog << time_string << " ["
<< level_colour (level) << level_colour (level)
<< std::setw (trunc_cast<int> (level_width ())) << std::setw (util::cast::lossless<int> (level_width ()))
<< std::left << std::left
<< level << level
<< std::setw (0) << std::setw (0)

View File

@ -127,7 +127,7 @@ paged::release (char *desired)
// bail if the region is alread unmapped, or if it's not sufficiently // bail if the region is alread unmapped, or if it's not sufficiently
// behind the current cursor. // behind the current cursor.
if (desired >= m_cursor || sign_cast<size_t> (m_cursor - desired) < m_window) if (desired >= m_cursor || util::cast::sign<size_t> (m_cursor - desired) < m_window)
return; return;
desired += m_window; desired += m_window;

View File

@ -28,7 +28,7 @@ util::memory::pagesize (void)
static size_t val; static size_t val;
if (!val) { if (!val) {
val = sign_cast<unsigned long> (posix::error::try_value (sysconf (_SC_PAGE_SIZE))); val = util::cast::sign<unsigned long> (posix::error::try_value (sysconf (_SC_PAGE_SIZE)));
} }
return val; return val;

View File

@ -93,5 +93,5 @@ int
util::parse<int> (util::view<const char*> str) util::parse<int> (util::view<const char*> str)
{ {
auto intermediate = util::parse<long> (str); auto intermediate = util::parse<long> (str);
return trunc_cast<int> (intermediate); return util::cast::lossless<int> (intermediate);
} }

View File

@ -28,8 +28,8 @@ util::sleep (uint64_t ns)
{ {
struct timespec req, rem; struct timespec req, rem;
req.tv_sec = trunc_cast<time_t> (ns / SECOND); req.tv_sec = util::cast::lossless<time_t> (ns / SECOND);
req.tv_nsec = trunc_cast<long > (ns % SECOND); req.tv_nsec = util::cast::lossless<long > (ns % SECOND);
while (nanosleep (&req, &rem)) { while (nanosleep (&req, &rem)) {
req = rem; req = rem;