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.
|
// (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;
|
||||||
|
@ -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
217
cast.hpp
@ -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
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
6
io.cpp
@ -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);
|
||||||
|
@ -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 ();
|
||||||
|
2
log.cpp
2
log.cpp
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user