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.
record record;
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;
return ptr;

View File

@ -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<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);
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<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)
os << frames[i] << '\t' << names.get()[i] << '\t' << addr2line (frames[i]);

217
cast.hpp
View File

@ -23,113 +23,124 @@
#include <limits>
///////////////////////////////////////////////////////////////////////////////
/// 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<T>::value &&
std::is_signed<U>::value,
T
>
sign_cast (const U 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);
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<T>::value &&
std::is_signed<U>::value,
T
>
sign (const U u)
{
CHECK_GE (u, 0);
return static_cast<T> (u);
}
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
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
trunc_cast (const SrcT src)
{
return static_cast<DstT> (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 <typename T, typename V>
T*
known_cast (V *v)
{
CHECK (dynamic_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);
}
///////////////////////////////////////////////////////////////////////////
/// 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>
T*
known (V *v)
{
CHECK (dynamic_cast<T*> (v));
return static_cast<T*> (v);
}
//-------------------------------------------------------------------------
template <typename T, typename V>
T&
known (V &v)
{
CHECK_NOTHROW (dynamic_cast<T> (v));
return static_cast<T> (v);
}
};
#endif

View File

@ -456,13 +456,13 @@ parser::print_help (const int argc,
else
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))
std::cout << std::get<std::string> (*l) << '\t';
else
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)
<< '\n';
}

View File

@ -41,7 +41,7 @@ retry:
if (written < 0)
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);
goto retry;
}

View File

@ -257,25 +257,25 @@ SHA1::digest (void) const
CHECK_EQ (+state, +FINISHED);
return { {
trunc_cast<uint8_t> ((H[0] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[0] ) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[1] ) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[2] ) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[3] ) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[4] ) & 0xFF)
util::cast::lossless<uint8_t> ((H[0] >> 24u) & 0xFF),
util::cast::lossless<uint8_t> ((H[0] >> 16u) & 0xFF),
util::cast::lossless<uint8_t> ((H[0] >> 8u) & 0xFF),
util::cast::lossless<uint8_t> ((H[0] ) & 0xFF),
util::cast::lossless<uint8_t> ((H[1] >> 24u) & 0xFF),
util::cast::lossless<uint8_t> ((H[1] >> 16u) & 0xFF),
util::cast::lossless<uint8_t> ((H[1] >> 8u) & 0xFF),
util::cast::lossless<uint8_t> ((H[1] ) & 0xFF),
util::cast::lossless<uint8_t> ((H[2] >> 24u) & 0xFF),
util::cast::lossless<uint8_t> ((H[2] >> 16u) & 0xFF),
util::cast::lossless<uint8_t> ((H[2] >> 8u) & 0xFF),
util::cast::lossless<uint8_t> ((H[2] ) & 0xFF),
util::cast::lossless<uint8_t> ((H[3] >> 24u) & 0xFF),
util::cast::lossless<uint8_t> ((H[3] >> 16u) & 0xFF),
util::cast::lossless<uint8_t> ((H[3] >> 8u) & 0xFF),
util::cast::lossless<uint8_t> ((H[3] ) & 0xFF),
util::cast::lossless<uint8_t> ((H[4] >> 24u) & 0xFF),
util::cast::lossless<uint8_t> ((H[4] >> 16u) & 0xFF),
util::cast::lossless<uint8_t> ((H[4] >> 8u) & 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) {
ssize_t consumed = posix::error::try_value (::write (out, cursor, remaining));
remaining -= sign_cast<size_t> (consumed);
cursor += sign_cast<size_t> (consumed);
remaining -= util::cast::sign<size_t> (consumed);
cursor += util::cast::sign<size_t> (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<std::streamsize> (m_indent.size ()));
m_dest->sputn (m_indent.data (), util::cast::sign<std::streamsize> (m_indent.size ()));
m_line_start = ch == '\n';
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;
::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);
if (m_data == MAP_FAILED)
::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 << " ["
<< level_colour (level)
<< std::setw (trunc_cast<int> (level_width ()))
<< std::setw (util::cast::lossless<int> (level_width ()))
<< std::left
<< level
<< 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
// 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;
desired += m_window;

View File

@ -28,7 +28,7 @@ util::memory::pagesize (void)
static size_t 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;

View File

@ -93,5 +93,5 @@ int
util::parse<int> (util::view<const char*> 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;
req.tv_sec = trunc_cast<time_t> (ns / SECOND);
req.tv_nsec = trunc_cast<long > (ns % SECOND);
req.tv_sec = util::cast::lossless<time_t> (ns / SECOND);
req.tv_nsec = util::cast::lossless<long > (ns % SECOND);
while (nanosleep (&req, &rem)) {
req = rem;