cast: move casting functions into util::cast namespace
This commit is contained in:
parent
c6d025453a
commit
1d11f05918
@ -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;
|
||||
|
@ -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
217
cast.hpp
@ -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
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
6
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<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);
|
||||
|
@ -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 ();
|
||||
|
2
log.cpp
2
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<int> (level_width ()))
|
||||
<< std::setw (util::cast::lossless<int> (level_width ()))
|
||||
<< std::left
|
||||
<< level
|
||||
<< std::setw (0)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user