refactor: extract int_power / integer_rep_width_v to dedicated header; include double_width_int only when emulation is needed

Splits the two tiny utilities that both `double_width_int` and `fixed_point` depend on into
`bits/int_power.h`, so neither has to include the other for them.  With that dependency cut,
`fixed_point.h` now includes `double_width_int.h` only when `__SIZEOF_INT128__` is not
defined — i.e. exactly on the platforms where `int128_t` / `uint128_t` are dwint aliases.
On compilers with native `__int128` the dwint definition is no longer parsed by every
translation unit that pulls in `fixed_point.h`.

The `is_signed_v<double_width_int<T>>` specialization moves inside the same conditional so
it is only declared when dwint is actually visible.
This commit is contained in:
Mateusz Pusz
2026-05-23 23:36:43 +02:00
parent fc7eb739e7
commit 4ec5b655b0
4 changed files with 95 additions and 41 deletions
+1
View File
@@ -33,6 +33,7 @@ add_mp_units_module(
include/mp-units/bits/core_gmf.h
include/mp-units/bits/double_width_int.h
include/mp-units/bits/fixed_point.h
include/mp-units/bits/int_power.h
include/mp-units/bits/get_associated_quantity.h
include/mp-units/bits/hacks.h
include/mp-units/bits/module_macros.h
@@ -22,57 +22,24 @@
#pragma once
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/int_power.h>
#include <mp-units/ext/type_traits.h> // IWYU pragma: keep
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <algorithm>
#include <bit>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <tuple>
#include <type_traits>
#include <version>
// <cmath> becomes partially freestanding in C++26
// before that, there is no guarantee about this header even existing
// (GCC 14 has it, but actively #error's out)
#if __STDC_HOSTED__
#include <cmath>
#endif
#endif
#endif
namespace mp_units::detail {
template<typename T>
constexpr std::size_t integer_rep_width_v = std::numeric_limits<std::make_unsigned_t<T>>::digits;
template<std::floating_point T>
[[nodiscard]] consteval T int_power(T base, int exponent)
{
#if __STDC_HOSTED__ && defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202202L
return std::ldexp(base, exponent);
#else
if (exponent < 0) {
base = T{1} / base;
exponent = -exponent;
}
T ret = 1;
while (exponent) {
if (exponent & 1) ret *= base;
exponent >>= 1;
base *= base;
}
return ret;
#endif
}
// A double-width integer synthesized from two base-width integers. On platforms without a
// native `__int128` (notably MSVC), `int128_t` / `uint128_t` are aliases for this template
// instantiated on (u)int64_t; the operator set is therefore comprehensive enough for it to
+15 -5
View File
@@ -22,10 +22,16 @@
#pragma once
#include <mp-units/bits/double_width_int.h>
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/int_power.h>
#include <mp-units/ext/type_traits.h> // IWYU pragma: keep
// `double_width_int` only emulates `int128_t` on platforms without native `__int128` (notably
// MSVC). On every other platform `int128_t` is the builtin and dwint is unused in library code
#if !defined(__SIZEOF_INT128__)
#include <mp-units/bits/double_width_int.h>
#endif
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#ifdef MP_UNITS_IMPORT_STD
import std;
@@ -74,9 +80,6 @@ constexpr std::size_t max_native_width = 64;
template<typename T>
constexpr bool is_signed_v = std::is_signed_v<T>;
template<typename T>
constexpr bool is_signed_v<double_width_int<T>> = double_width_int<T>::is_signed;
#if defined(__SIZEOF_INT128__)
MP_UNITS_DIAGNOSTIC_PUSH
@@ -95,6 +98,13 @@ inline constexpr bool is_signed_v<unsigned __int128> = false;
MP_UNITS_DIAGNOSTIC_POP
#else
// Only needed when `double_width_int` doubles as `int128_t`; the dwint header was included
// for that purpose above.
template<typename T>
constexpr bool is_signed_v<double_width_int<T>> = double_width_int<T>::is_signed;
#endif
template<typename T>
@@ -0,0 +1,76 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <concepts>
#include <cstddef>
#include <limits>
#include <type_traits>
#include <version>
// <cmath> becomes partially freestanding in C++26
// before that, there is no guarantee about this header even existing
// (GCC 14 has it, but actively #error's out)
#if __STDC_HOSTED__
#include <cmath>
#endif
#endif
#endif
namespace mp_units::detail {
// Representation width (in bits) of an integral type, as the digit count of its unsigned
// counterpart. Lives in its own header so that both `double_width_int.h` and
// `fixed_point.h` can use it without one having to include the other.
template<typename T>
constexpr std::size_t integer_rep_width_v = std::numeric_limits<std::make_unsigned_t<T>>::digits;
// `2^exponent` (or more precisely `base * 2^exponent`) as a compile-time floating-point
// constant. Used by `double_width_int`'s long-double conversions and by `fixed_point`'s
// long-double constructor.
template<std::floating_point T>
[[nodiscard]] consteval T int_power(T base, int exponent)
{
#if __STDC_HOSTED__ && defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202202L
return std::ldexp(base, exponent);
#else
if (exponent < 0) {
base = T{1} / base;
exponent = -exponent;
}
T ret = 1;
while (exponent) {
if (exponent & 1) ret *= base;
exponent >>= 1;
base *= base;
}
return ret;
#endif
}
} // namespace mp_units::detail