Skip to content

Commit

Permalink
Make sign a proper enum class
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Aug 11, 2024
1 parent c6c830e commit 993f56c
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 48 deletions.
23 changes: 5 additions & 18 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -2051,20 +2051,6 @@ FMT_END_EXPORT
// between clang and gcc on ARM (#1919).
FMT_EXPORT using format_args = basic_format_args<format_context>;

// We cannot use enum classes as bit fields because of a gcc bug, so we put them
// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
// Additionally, if an underlying type is specified, older gcc incorrectly warns
// that the type is too small. Both bugs are fixed in gcc 9.3.
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
# define FMT_ENUM_UNDERLYING_TYPE(type)
#else
# define FMT_ENUM_UNDERLYING_TYPE(type) : type
#endif
namespace sign {
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
}
using sign_t = sign::type;

namespace detail {

template <typename T, typename Enable = void>
Expand Down Expand Up @@ -2115,6 +2101,7 @@ enum class presentation_type : unsigned char {
};

enum class align { none, left, right, center, numeric };
enum class sign { none, minus, plus, space };

// Basic format specifiers for built-in and string types.
class basic_specs {
Expand Down Expand Up @@ -2201,11 +2188,11 @@ class basic_specs {
return (data_ & (width_mask | precision_mask)) != 0;
}

constexpr auto sign() const -> sign_t {
return static_cast<sign_t>((data_ & sign_mask) >> sign_shift);
constexpr auto sign() const -> sign {
return static_cast<fmt::sign>((data_ & sign_mask) >> sign_shift);
}
FMT_CONSTEXPR void set_sign(sign_t a) {
data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(a) << sign_shift);
FMT_CONSTEXPR void set_sign(fmt::sign s) {
data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(s) << sign_shift);
}

constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; }
Expand Down
60 changes: 30 additions & 30 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1130,11 +1130,12 @@ inline auto digits2(size_t value) -> const char* {
}

// Sign is a template parameter to workaround a bug in gcc 4.8.
template <typename Char, typename Sign> constexpr auto sign(Sign s) -> Char {
template <typename Char, typename Sign> constexpr auto getsign(Sign s) -> Char {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
static_assert(std::is_same<Sign, sign_t>::value, "");
static_assert(std::is_same<Sign, sign>::value, "");
#endif
return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> (s * 8));
return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >>
(static_cast<int>(s) * 8));
}

template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
Expand Down Expand Up @@ -2136,7 +2137,7 @@ template <typename UInt> struct write_int_arg {
};

template <typename T>
FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
FMT_CONSTEXPR auto make_write_int_arg(T value, sign s)
-> write_int_arg<uint32_or_64_or_128_t<T>> {
auto prefix = 0u;
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
Expand All @@ -2146,7 +2147,7 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
} else {
constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
prefix = prefixes[sign];
prefix = prefixes[static_cast<int>(s)];
}
return {abs_value, prefix};
}
Expand Down Expand Up @@ -2363,19 +2364,19 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,

template <typename Char, typename OutputIt>
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
format_specs specs, sign_t sign)
-> OutputIt {
format_specs specs, sign s) -> OutputIt {
auto str =
isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf");
constexpr size_t str_size = 3;
auto size = str_size + (sign ? 1 : 0);
auto size = str_size + (s != sign::none ? 1 : 0);
// Replace '0'-padding with space for non-finite values.
const bool is_zero_fill =
specs.fill_size() == 1 && specs.fill_unit<Char>() == '0';
if (is_zero_fill) specs.set_fill(' ');
return write_padded<Char>(out, specs, size,
[=](reserve_iterator<OutputIt> it) {
if (sign) *it++ = detail::sign<Char>(sign);
if (s != sign::none)
*it++ = detail::getsign<Char>(s);
return copy<Char>(str, str + str_size, it);
});
}
Expand Down Expand Up @@ -2486,12 +2487,12 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
template <typename Char, typename OutputIt, typename DecimalFP,
typename Grouping = digit_grouping<Char>>
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
const format_specs& specs, sign_t sign,
const format_specs& specs, sign s,
locale_ref loc) -> OutputIt {
auto significand = f.significand;
int significand_size = get_significand_size(f);
const Char zero = static_cast<Char>('0');
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0);
using iterator = reserve_iterator<OutputIt>;

Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc)
Expand Down Expand Up @@ -2523,7 +2524,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
char exp_char = specs.upper() ? 'E' : 'e';
auto write = [=](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
if (s != sign::none) *it++ = detail::getsign<Char>(s);
// Insert a decimal point after the first digit and add an exponent.
it = write_significand(it, significand, significand_size, 1,
decimal_point);
Expand Down Expand Up @@ -2551,7 +2552,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
auto grouping = Grouping(loc, specs.localized());
size += to_unsigned(grouping.count_separators(exp));
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
if (s != sign::none) *it++ = detail::getsign<Char>(s);
it = write_significand<Char>(it, significand, significand_size,
f.exponent, grouping);
if (!specs.alt()) return it;
Expand All @@ -2565,7 +2566,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
auto grouping = Grouping(loc, specs.localized());
size += to_unsigned(grouping.count_separators(exp));
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
if (s != sign::none) *it++ = detail::getsign<Char>(s);
it = write_significand(it, significand, significand_size, exp,
decimal_point, grouping);
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
Expand All @@ -2580,7 +2581,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
if (s != sign::none) *it++ = detail::getsign<Char>(s);
*it++ = zero;
if (!pointy) return it;
*it++ = decimal_point;
Expand All @@ -2605,14 +2606,13 @@ template <typename Char> class fallback_digit_grouping {

template <typename Char, typename OutputIt, typename DecimalFP>
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
const format_specs& specs, sign_t sign,
const format_specs& specs, sign s,
locale_ref loc) -> OutputIt {
if (is_constant_evaluated()) {
return do_write_float<Char, OutputIt, DecimalFP,
fallback_digit_grouping<Char>>(out, f, specs, sign,
loc);
fallback_digit_grouping<Char>>(out, f, specs, s, loc);
} else {
return do_write_float<Char>(out, f, specs, sign, loc);
return do_write_float<Char>(out, f, specs, s, loc);
}
}

Expand Down Expand Up @@ -3464,14 +3464,14 @@ template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
locale_ref loc) -> OutputIt {
// Use signbit because value < 0 is false for NaN.
sign_t sign = detail::signbit(value) ? sign::minus : specs.sign();
sign s = detail::signbit(value) ? sign::minus : specs.sign();

if (!detail::isfinite(value))
return write_nonfinite<Char>(out, detail::isnan(value), specs, sign);
return write_nonfinite<Char>(out, detail::isnan(value), specs, s);

if (specs.align() == align::numeric && sign) {
*out++ = detail::sign<Char>(sign);
sign = sign::none;
if (specs.align() == align::numeric && s != sign::none) {
*out++ = detail::getsign<Char>(s);
s = sign::none;
if (specs.width != 0) --specs.width;
}

Expand All @@ -3483,13 +3483,13 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
// Use Dragonbox for the shortest format.
using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
return write_float<Char>(out, dec, specs, sign, loc);
return write_float<Char>(out, dec, specs, s, loc);
}
}

memory_buffer buffer;
if (specs.type() == presentation_type::hexfloat) {
if (sign) buffer.push_back(detail::sign<char>(sign));
if (s != sign::none) buffer.push_back(detail::getsign<char>(s));
format_hexfloat(convert_float(value), specs, buffer);
return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()},
specs);
Expand All @@ -3511,7 +3511,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,

specs.precision = precision;
auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
return write_float<Char>(out, f, specs, sign, loc);
return write_float<Char>(out, f, specs, s, loc);
}

template <typename Char, typename OutputIt, typename T,
Expand All @@ -3530,17 +3530,17 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
if (is_constant_evaluated()) return write<Char>(out, value, format_specs());
if (const_check(!is_supported_floating_point(value))) return out;

auto sign = detail::signbit(value) ? sign::minus : sign_t::none;
auto s = detail::signbit(value) ? sign::minus : sign::none;

constexpr auto specs = format_specs();
using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
floaty_uint mask = exponent_mask<floaty>();
if ((bit_cast<floaty_uint>(value) & mask) == mask)
return write_nonfinite<Char>(out, std::isnan(value), specs, sign);
return write_nonfinite<Char>(out, std::isnan(value), specs, s);

auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
return write_float<Char>(out, dec, specs, sign, {});
return write_float<Char>(out, dec, specs, s, {});
}

template <typename Char, typename OutputIt, typename T,
Expand Down

0 comments on commit 993f56c

Please sign in to comment.