Cast helpers which provide more type information or do additional checks.
Motivation
C++ offers four different casts: static_cast
, const_cast
, reinterpret_cast
and dynamic_cast
which cover a wide area of explicit conversions. Some conversions can only be done by casts, e.g. dynamic_cast
to cast in a polymorphic class hierarchy or reinterpret_cast
to access the bytes of an object. static_cast
can be used to reverse implicit conversions or make implicit conversions explicit. Therefore, static_cast
does too many things at once, often hiding the intent of the programmer. Also, static_cast
involves no checking which can be undesirable.
In order to fix these shortcomings, this module provides special casts, which highlight the intent of the programmer, and casts that check for truncation at compile time or at runtime.
Special Casts
Special casts are used to replace static_cast
and reinterpret_cast
where possible:
Here is a small example:
float const f{3.5F};
int const i{fcppt::cast::float_to_int<int>(f)};
std::cout << i << '\n';
An important thing to note is that these casts can't be used with the wrong types on accident:
template <typename T>
void test(T const _t)
{
}
void g()
{
test(4);
}
Safe conversions
fcppt::cast::safe_numeric is a cast that doesn't do any runtime checking (as opposed to fcppt::cast::truncation_check). Instead, it checks at compile time which conversions between arithmetic types are safe. See fcppt::cast::safe_numeric for a detailed description on which conditions must hold in order for the cast to compile.
The following example shows how a class's constructor can be strengthened by fcppt::cast::safe_numeric:
class my_class
{
public:
using int_type = unsigned long;
template <typename Other>
explicit my_class(Other
const &_other) : inner_(
fcppt::cast::safe_numeric<int_type>(_other))
{
}
private:
int_type inner_;
};
Here are some examples of how the constructor can and can't be called:
my_class const test1(1U);
Checking truncation
fcppt::cast::truncation_check complements fcppt::cast::safe_numeric by checking at runtime if a conversion truncates.
In the following example, it is tested if the biggest unsigned long
value fits into an unsigned int
. This might be true, depending on the architecture.
void check_int_long()
{
auto const result(fcppt::cast::truncation_check<unsigned>(
std::numeric_limits<unsigned long
>::max()));
std::cout << "The casted value is " << result << '\n';
}
Truncating conversions of negative values are also detected.
void negative_conversion()
{
auto const result(fcppt::cast::truncation_check<unsigned>(-1));
std::cout << "The casted value is " << result << '\n';
}
Dynamic casting
dynamic_cast
can cast between pointers or references to objects in a class hierarchy. The cast can fail at runtime in which case the null pointer is returned for pointers and bad_cast
is thrown for references. fcppt::cast::dynamic returns an empty optional on failure instead:
struct base
{
base() = default;
virtual ~base() = default;
};
struct derived1 : base
{
derived1() = default;
~derived1() override = default;
};
struct derived2 : base
{
derived2() = default;
~derived2() override = default;
};
{
std::cout << to_d2.
has_value() <<
' ' << to_d1.has_value() <<
'\n';
}
To catch more mistakes, fcppt::cast::dynamic only works on related types: The type to cast to must inherit from the source type. In case you need to cast between unrelated types, fcppt::cast::dynamic_cross can be used:
struct base
{
base() = default;
virtual ~base() = default;
};
struct derived1 : base
{
derived1() = default;
~derived1() override = default;
};
struct derived2 : base
{
derived2() = default;
~derived2() override = default;
};
{
}
Casting user-defined types
For user-defined types static_cast and all of the casts from fcppt::cast can't be used directly. Instead, custom cast functions have to be defined. For example, fcppt.math defines functions called structure_cast to cast between vectors, dims and matrices. To incorporate cast functions from fcppt::cast, an additional template argument has to be added to the function's signature which then will be used to cast each element of the user-defined structure. For this purpose, this module defines several function objects suffixed with _fun, for example fcppt::cast::float_to_int_fun.
auto const res(
fcppt::math::vector::structure_cast<vec_2i, fcppt::cast::float_to_int_fun>(vec_2f(1.F, 2.F)));
std::cout << res << '\n';
|
template<typename Type > |
using | fcppt::cast::promote_int_type = typename fcppt::cast::detail::promote_int_type< Type >::type |
| The promoted type of an integral type.
|
|
|
template<typename Fun , typename Res , typename Src > |
constexpr decltype(auto) | fcppt::cast::apply (Src &&_src) |
| Applies a cast to a source.
|
|
template<typename Derived , typename Base , typename = std::enable_if_t< fcppt::type_traits::is_base_of<std::remove_cv_t<Base>, std::remove_cv_t<Derived>>::value>> |
fcppt::optional::reference< Derived > | fcppt::cast::dynamic (Base &_base) noexcept |
| Converts between references of related types using dynamic_cast , returning an empty optional on failure.
|
|
template<typename Derived , typename Base > |
fcppt::optional::reference< Derived > | fcppt::cast::dynamic_any (Base &_base) noexcept |
| Converts between references using dynamic_cast , returning an empty optional on failure.
|
|
template<typename Dest , typename Src > |
fcppt::optional::reference< Dest > | fcppt::cast::dynamic_cross (Src &_src) noexcept |
| Tries a dynamic_cast on unrelated types, returning an empty optional on failure.
|
|
template<typename Dest , typename Enum , typename = std::enable_if_t<std::conjunction_v<std::is_integral<Dest>>, std::is_enum<Enum>>> |
constexpr Dest | fcppt::cast::enum_to_int (Enum const _enum) noexcept |
| Converts an enum to an int.
|
|
template<typename Enum , typename = std::enable_if<std::is_enum_v<Enum>>> |
constexpr std::underlying_type_t< Enum > | fcppt::cast::enum_to_underlying (Enum const _enum) noexcept |
| Converts an enum to its underlying type.
|
|
template<typename Dest , typename Source > |
constexpr Dest | fcppt::cast::float_to_int (Source const _source) noexcept |
| Converts a float to a signed int.
|
|
template<typename Dest , typename Source > |
Dest | fcppt::cast::from_void_ptr (Source *const _ptr) noexcept |
| Converts a void pointer to a different pointer.
|
|
template<typename Enum , typename Source , typename = std::enable_if_t<std::conjunction_v<std::is_enum<Enum>, std::is_integral<Source>>>> |
constexpr Enum | fcppt::cast::int_to_enum (Source const _source) noexcept |
| Converts an int to an enum.
|
|
template<typename Dest , typename Source > |
constexpr Dest | fcppt::cast::int_to_float (Source const _source) noexcept |
| Converts an int to a float.
|
|
template<typename Type > |
constexpr fcppt::cast::promote_int_type< Type > | fcppt::cast::promote_int (Type const &_value) |
| Promotes an integral type to int or unsigned int.
|
|
template<typename Dest , typename Source > |
constexpr Dest | fcppt::cast::safe_numeric (Source const &_source) noexcept |
| Safe numeric cast is a safer static_cast that forbids lossy conversions.
|
|
template<typename Dest , typename Source > |
constexpr Dest | fcppt::cast::size (Source const _source) noexcept |
| Converts a type to a similar type of different size.
|
|
template<typename Derived , typename Base > |
std::enable_if_t< std::is_reference_v< Derived >, Derived > | fcppt::cast::static_downcast (Base &_source) noexcept |
| Statically converts a reference to a base class to a reference to a derived class.
|
|
template<typename Dest , typename Source > |
Dest | fcppt::cast::to_char_ptr (Source *const _source) noexcept |
| Converts a pointer to a pointer to characters.
|
|
template<typename Type > |
constexpr std::make_signed_t< Type > | fcppt::cast::to_signed (Type const _value) noexcept |
| Converts an unsigned int to its signed type.
|
|
template<typename Source > |
std::uintptr_t | fcppt::cast::to_uint_ptr (Source *const _ptr) noexcept |
| Converts a pointer to a std::uintptr_t .
|
|
template<typename Type > |
constexpr std::make_unsigned_t< Type > | fcppt::cast::to_unsigned (Type const _value) noexcept |
| Converts a signed int to its unsigned type.
|
|
template<typename Type > |
void | fcppt::cast::to_void (Type const &_value) noexcept |
| Casts an expression to void.
|
|
template<typename Source > |
void const * | fcppt::cast::to_void_ptr (Source const *const _ptr) noexcept |
| Converts a pointer to const to a pointer to const void.
|
|
template<typename Source > |
void * | fcppt::cast::to_void_ptr (Source *const _ptr) noexcept |
| Converts a pointer to a pointer to void.
|
|
template<typename Dest , typename Source , typename = std::enable_if_t<std::conjunction_v<std::is_integral<Source>, std::is_integral<Dest>>>> |
fcppt::optional::object< Dest > | fcppt::cast::truncation_check (Source const _source) |
| Cast between integral types, checking for truncation.
|
|
◆ promote_int_type
The promoted type of an integral type.
- Template Parameters
-
Type | Must be an integral type. |
◆ apply()
template<typename Fun , typename Res , typename Src >
constexpr decltype(auto) fcppt::cast::apply |
( |
Src && |
_src | ) |
|
|
constexpr |
Applies a cast to a source.
◆ dynamic()
template<typename Derived , typename Base , typename = std::enable_if_t< fcppt::type_traits::is_base_of<std::remove_cv_t<Base>, std::remove_cv_t<Derived>>::value>>
Converts between references of related types using dynamic_cast
, returning an empty optional on failure.
Tries to cast _src to Dest using dynamic_cast
. On failure, an empty optional is returned. To catch more mistakes, Base must be a base class of Derived. In case you need a cross cast, use fcppt::cast::dynamic_cross.
Here is an example:
struct base
{
base() = default;
virtual ~base() = default;
};
struct derived1 : base
{
derived1() = default;
~derived1() override = default;
};
struct derived2 : base
{
derived2() = default;
~derived2() override = default;
};
{
std::cout << to_d2.
has_value() <<
' ' << to_d1.has_value() <<
'\n';
}
- Template Parameters
-
Derived | The type to cast to. Can be cv-qualified. Must inherit from Base. |
Base | A cv-qualified non-reference type. |
- See also
- fcppt::cast::dynamic_cross
◆ dynamic_any()
template<typename Derived , typename Base >
Converts between references using dynamic_cast
, returning an empty optional on failure.
Tries to cast _src to Dest using dynamic_cast
. On failure, an empty optional is returned.
- Template Parameters
-
Derived | The type to cast to. Can be cv-qualified. |
Base | A cv-qualified non-reference type. |
◆ dynamic_cross()
template<typename Dest , typename Src >
Tries a dynamic_cast
on unrelated types, returning an empty optional on failure.
This cast is the same as fcppt::cast::dynamic but only works on unrelated types.
Here is an example:
struct base
{
base() = default;
virtual ~base() = default;
};
struct derived1 : base
{
derived1() = default;
~derived1() override = default;
};
struct derived2 : base
{
derived2() = default;
~derived2() override = default;
};
{
}
- Template Parameters
-
Dest | The type to cast to. Can be cv-qualified. Must not inherit from Base. |
Src | A cv-qualified non-reference type. |
- See also
- fcppt::cast::dynamic
◆ enum_to_int()
template<typename Dest , typename Enum , typename = std::enable_if_t<std::conjunction_v<std::is_integral<Dest>>, std::is_enum<Enum>>>
constexpr Dest fcppt::cast::enum_to_int |
( |
Enum const |
_enum | ) |
|
|
inlineconstexprnoexcept |
Converts an enum to an int.
Converts _enum to the integer type specified by Dest. This cast is unsafe and should only be used if the enum value can be converted to the destination type. Consider fcppt::cast::enum_to_underlying instead.
- Template Parameters
-
Dest | Must be an integral type |
Enum | Must be an enumeration type |
◆ enum_to_underlying()
template<typename Enum , typename = std::enable_if<std::is_enum_v<Enum>>>
constexpr std::underlying_type_t< Enum > fcppt::cast::enum_to_underlying |
( |
Enum const |
_enum | ) |
|
|
inlineconstexprnoexcept |
Converts an enum to its underlying type.
Converts _enum to its underlying integer type. This cast is safe.
- Template Parameters
-
Enum | Must be an enumeration type |
◆ float_to_int()
template<typename Dest , typename Source >
constexpr Dest fcppt::cast::float_to_int |
( |
Source const |
_source | ) |
|
|
inlineconstexprnoexcept |
Converts a float to a signed int.
Converts _source to an integer. The function ensures that only signed integers can be used as destination types. If you need to cast to unsigned integers, use fcppt::cast::to_unsigned in addition. This cast is unsafe and should be used with care.
- Template Parameters
-
Source | Must be a floating point type |
Dest | Must be a signed integer type |
◆ from_void_ptr()
template<typename Dest , typename Source >
Dest fcppt::cast::from_void_ptr |
( |
Source *const |
_ptr | ) |
|
|
inlinenoexcept |
Converts a void pointer to a different pointer.
Converts the void pointer _ptr to the pointer type specified by Dest. This cast is unsafe.
- Template Parameters
-
Source | Must be a pointer type to (cv) void |
Dest | Must be a pointer type |
◆ int_to_enum()
template<typename Enum , typename Source , typename = std::enable_if_t<std::conjunction_v<std::is_enum<Enum>, std::is_integral<Source>>>>
constexpr Enum fcppt::cast::int_to_enum |
( |
Source const |
_source | ) |
|
|
inlineconstexprnoexcept |
Converts an int to an enum.
Converts the integer _source to the enum type specified by Enum. This cast is unsafe and should only be used if the enum can actually hold the integer value. Consider using fcppt::cast_to_enum instead.
- Template Parameters
-
Source | Must be an integral type |
Enum | Must be an enumeration type |
◆ int_to_float()
template<typename Dest , typename Source >
constexpr Dest fcppt::cast::int_to_float |
( |
Source const |
_source | ) |
|
|
inlineconstexprnoexcept |
Converts an int to a float.
Converts the integer _source to the float type specified by Dest by truncating like static_cast
. This cast is unsafe and should be used with care.
- Template Parameters
-
Source | Must be an integral type |
Dest | Must be a floating point type |
◆ promote_int()
Promotes an integral type to int or unsigned int.
- Template Parameters
-
Type | Must be an integral type. |
◆ safe_numeric()
template<typename Dest , typename Source >
constexpr Dest fcppt::cast::safe_numeric |
( |
Source const & |
_source | ) |
|
|
constexprnoexcept |
Safe numeric cast is a safer static_cast
that forbids lossy conversions.
This cast converts one arithmetic type to another, where the size of the destination type must be at least the size of the source type.
Furthermore, the conversion is only allowed if and only if one of the following cases hold true:
-
Both types are the same type
-
Both types are integer types and both have the same signedness
-
Both types are floating point types
- Template Parameters
-
Dest | The destination type of the conversion |
Source | The source type of the conversion |
◆ size()
template<typename Dest , typename Source >
constexpr Dest fcppt::cast::size |
( |
Source const |
_source | ) |
|
|
inlineconstexprnoexcept |
Converts a type to a similar type of different size.
Converts _source to the type specified by Dest. Exactly one of the following cases must hold:
-
Both types are signed integer types.
-
Both types are unsigned integer types.
-
Both types are floating point types.
◆ static_downcast()
template<typename Derived , typename Base >
std::enable_if_t< std::is_reference_v< Derived >, Derived > fcppt::cast::static_downcast |
( |
Base & |
_source | ) |
|
|
noexcept |
Statically converts a reference to a base class to a reference to a derived class.
Converts _source to the reference type specified by Derived. This cast is unsafe and should only be used if the _source has a dynamic type which is a subtype of Derived. Consider using fcppt::cast::dynamic instead.
Derived must be a reference to a class type derived from Base.
- Template Parameters
-
Derived | The type to cast to. Must be a reference type. Must inherit from Base. |
Base | A cv-qualified non-reference type. |
◆ to_char_ptr()
template<typename Dest , typename Source >
Dest fcppt::cast::to_char_ptr |
( |
Source *const |
_source | ) |
|
|
inlinenoexcept |
Converts a pointer to a pointer to characters.
Converts _source to the pointer to character type specified by Dest. This cast can be used to access the byte representation of an object, e.g. for serialization, and largely replaces reinterpret_cast
.
- Template Parameters
-
Dest | Must be a pointer to (cv) unsigned char |
◆ to_signed()
template<typename Type >
constexpr std::make_signed_t< Type > fcppt::cast::to_signed |
( |
Type const |
_value | ) |
|
|
inlineconstexprnoexcept |
Converts an unsigned int to its signed type.
Converts _value to its signed type. This cast is unsafe and should only be used if _value fits into the result.
- Template Parameters
-
Type | Must be an unsigned type |
◆ to_uint_ptr()
template<typename Source >
std::uintptr_t fcppt::cast::to_uint_ptr |
( |
Source *const |
_ptr | ) |
|
|
inlinenoexcept |
Converts a pointer to a std::uintptr_t
.
◆ to_unsigned()
template<typename Type >
constexpr std::make_unsigned_t< Type > fcppt::cast::to_unsigned |
( |
Type const |
_value | ) |
|
|
inlineconstexprnoexcept |
Converts a signed int to its unsigned type.
Converts _value to its unsigned type. This cast is unsafe and should only be used if _value is positive.
- Template Parameters
-
Type | Must be a signed type |
◆ to_void()
template<typename Type >
void fcppt::cast::to_void |
( |
Type const & |
_value | ) |
|
|
inlinenoexcept |
Casts an expression to void.
Casts _value to void, ignoring the expression. This is useful if you want to avoid using an expression, for example if an expression is used to do static type checking (e.g. for completeness).
- Template Parameters
-
Type | Can be any object type |
- Parameters
-
_value | The value to cast to void |
◆ to_void_ptr() [1/2]
template<typename Source >
void * fcppt::cast::to_void_ptr |
( |
Source *const |
_ptr | ) |
|
|
inlinenoexcept |
Converts a pointer to a pointer to void.
◆ to_void_ptr() [2/2]
template<typename Source >
void const * fcppt::cast::to_void_ptr |
( |
Source const *const |
_ptr | ) |
|
|
inlinenoexcept |
Converts a pointer to const to a pointer to const void.
◆ truncation_check()
template<typename Dest , typename Source , typename = std::enable_if_t<std::conjunction_v<std::is_integral<Source>, std::is_integral<Dest>>>>
Cast between integral types, checking for truncation.
Casts _source of type Source to the type Dest. It returns the converted value iff the conversion results in no truncation.
- Template Parameters
-
Dest | Must be an integral type |
Source | Must be an integral type |