Hello John,
Some time ago I asked whether further type traits would be included in boost. Unfortunately that somehow fizzled out.
Because based on this, many useful functions and classes can first be defined that behave correctly.
But the problems are much deeper and I would like to show a solution here.
1)
For some types there are feature-tesing-macros (e.g. __cpp_char8_t, __cpp_unicode_characters), but not for most. So this is incomplete. So we need this (plus the __cpp_lib-macros):
- __cpp_char8/16/32_t, __cpp charw_t, __cpp_char_t
- __cpp_int8/16/32/64/128_t
- __cpp_float16/32/64/80/128_t, __cpp_float16b_t
- __cpp_bool_t
2)
Building on this, we can then query, e.g.
struct has_float80: bool_constant
<
#if defined (__cpp_float80_t)
true
#else
false
#endif
> {};
template <typename Type>
struct is_float80: bool_constant
<
#if defined (__cpp_float80_t)
is_same_v<Type, float80>
#else
false
#endif
> {};
This has to be done for all types.
3)
Then we can further classify; plus is_X_v and concepts:
character:
template <typename Type>
struct is_character: bool_constant
<
#if defined (__cpp_char32_t)
is_same_v<Type, char32_t> ||
#elif defined (__cpp_char16_t)
is_same_v<Type, char16_t> ||
#elif defined (__cpp_char8_t)
is_same_v<Type, char8_t> ||
#elif defined (__cpp_charw_t)
is_same_v<Type, wchar_t> ||
#endif
is_same_v<Type, char>
> {};
template <typename Type>
struct is_unsigned_character: bool_constant
<
is_unsigned_v<Type> && character_v<Type>
> {};
template <typename Type>
is_signed_character: bool_constant
<
is_signed_v<Type> && character_v<Type>
> {};
float:
template <typename Type>
struct is_floating_point: bool_constant
<
#if defined (__cpp_float128_t)
is_same_v<Type, float128_t> ||
#elif defined (__cpp_float80_t)
is_same_v<Type, float80_t> ||
#elif defined (__cpp_float64_t)
is_same_v<Type, float64_t> ||
#elif defined (__cpp_float32_t)
is_same_v<Type, float32_t> ||
#elif defined (__cpp_float16b_t)
is_same_v<Type, float16b_t> ||
#elif defined (__cpp_float16_t)
is_same_v<Type, float16_t> ||
#endif
false
> {};
integer:
template <typename Type>
struct is_unsigned_integer: bool_constant
<
#if defined (__cpp_int128_t)
is_same_v<Type, uint128_t> ||
#elif defined (__cpp_int64_t)
is_same_v<Type, uint64_t> ||
#elif defined (__cpp_int32_t)
is_same_v<Type, uint32_t> ||
#elif defined (__cpp_int16_t)
is_same_v<Type, uint16_t> ||
#elif defined (__cpp_int8_t)
is_same_v<Type, uint8_t> ||
#endif
false
> {};
template <typename Type>
struct is_signed_integer: bool_constant
<
#if defined (__cpp_int128_t)
is_same_v<Type, int128_t> ||
#elif defined (__cpp_int64_t)
is_same_v<Type, int64_t> ||
#elif defined (__cpp_int32_t)
is_same_v<Type, int32_t> ||
#elif defined (__cpp_int16_t)
is_same_v<Type, int16_t> ||
#elif defined (__cpp_int8_t)
is_same_v<Type, int8_t> ||
#endif
false
> {};
template <typename Type>
struct is_integer: bool_constant
<
is_unsigned_integer_v<Type> || is_signed_integer_v<Type>
> {};
logic:
template <typename Type>
struct is_logic: bool_constant
<
is_same_v<Type, bool>
> {};
With that we would have created the basics to be able to continue working properly at all.
4)
3 more steps to make things easier
#define bitsof(ARG) (size_t(sizeof(ARG) * CHAR_BIT))
Like std::numbers with std::strings with various constantans
namespace strings
{
constexpr static auto empty = "";
constexpr static auto nullptr = "nullptr";
constexpr static auto Void = "void";
constexpr static auto True = "true";
constexpr static auto False = "false";
constexpr static auto indeterminate = "indeterminate";
constexpr static auto overdeterminate = "overdeterminate";
constexpr static auto underdeterminate = "underdeterminate";
constexpr static auto bin = "bin";
constexpr static auto oct = "oct";
constexpr static auto dec = "dec";
constexpr static auto hex = "hex";
constexpr static auto literal_bin = "0b";
constexpr static auto literal_oct = "0o";
constexpr static auto literal_dec = "0d";
constexpr static auto literal_hex = "0x";
}
and more
and for streams so that you don't have to fumble with the flags
namespace detail
{
using format_flags_t = typename std::ios_base::fmtflags;
static inline constexpr format_flags_t
defaultfloat = format_flags_t{},
hexfloat = std::ios_base::fixed | std::ios_base::scientific;
inline format_flags_t get_flags (const std::ios_base & stream, const format_flags_t & fieldmask) noexcept
{
return stream.flags () & fieldmask;
}
} // detail
inline bool is_boolalpha (const std::ios_base & stream) noexcept {return (stream.flags () & stream.boolalpha) == stream.boolalpha; }
inline bool is_showbase (const std::ios_base & stream) noexcept {return (stream.flags () & stream.showbase) == stream.showbase; }
inline bool is_showpoint (const std::ios_base & stream) noexcept {return (stream.flags () & stream.showpoint) == stream.showpoint; }
inline bool is_skip (const std::ios_base & stream) noexcept {return (stream.flags () & stream.skipws) == stream.skipws; }
inline bool is_uppercase (const std::ios_base & stream) noexcept {return (stream.flags () & stream.uppercase) == stream.uppercase; }
inline bool is_lowercase (const std::ios_base & stream) noexcept {return! is_uppercase (stream); }
inline bool is_float_bin (const std::ios_base & stream) noexcept {return false; }
inline bool is_float_hex (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.floatfield) == detail::hexfloat; }
inline bool is_float_fixed (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.floatfield) == stream.fixed; }
inline bool is_float_default (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.floatfield) == detail::defaultfloat; }
inline bool is_float_scientific (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.floatfield) == stream.scientific; }
inline bool is_integer_bin (const std::ios_base & stream) noexcept {return false; }
inline bool is_integer_oct (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.basefield) == stream.oct; }
inline bool is_integer_dec (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.basefield) == stream.dec; }
inline bool is_integer_hex (const std::ios_base & stream) noexcept {return detail::get_flags (stream, stream.basefield) == stream.hex; }
5)
And now we can finally get correctly working functions, e.g.
template <integer Type, unsigned_integer Bits>
inline constexpr Type shl(const Type& arg, const Bits& bits) noexcept
{
using unsigned_type = make_unsigned_t<Type>;
const type
res {Type(arg << unsigned_type(bits))};
return (bits < bitsof(Type)) ? res : Type{0};
}
template <integer Type, unsigned_integer Bits>
inline constexpr Type shr(const Type& arg, const Bits& bits) noexcept
{
using unsigned_type = make_unsigned_t<Type>;
const type
ovl {is_signed_integer_v<Type> ? ((arg < Type{0})? Type(-1) : Type{0}) : Type{0}},
res {Type(arg >> unsigned_type(bits))};
return (bits < bitsof(Type)) ? res : ovl;
}
and classes, e.g. constexpr bool2/3/4 with
template <> struct is_logic<bool4>: true_type {};
template <> struct is_logic<bool3>: true_type {};
template <> struct is_logic<bool2>: true_type {};
(I am happy to send it to you, because boost::tribool does not meet the requirements).
PS: I could have used the days multi-array/multi-index; but that's done because all of these libs are not constexpr. Will these be updated again?
thx
Gero