Merge commit 'ec04d9c03ddf12e46dca5348d4ca57b526e425f1'

This commit is contained in:
Mateusz Pusz
2023-06-14 21:13:07 +03:00
444 changed files with 18133 additions and 38078 deletions
+5 -2
View File
@@ -21,7 +21,7 @@
# SOFTWARE.
cmake_minimum_required(VERSION 3.19)
project(mp-units VERSION 0.8.0 LANGUAGES CXX)
project(mp-units VERSION 2.0.0 LANGUAGES CXX)
set(projectPrefix UNITS_)
@@ -44,10 +44,13 @@ add_subdirectory(core)
add_subdirectory(core-fmt)
add_subdirectory(core-io)
add_subdirectory(systems)
add_subdirectory(utility)
# project-wide wrapper
add_library(mp-units INTERFACE)
target_link_libraries(mp-units INTERFACE mp-units::core mp-units::core-io mp-units::core-fmt mp-units::systems)
target_link_libraries(
mp-units INTERFACE mp-units::core mp-units::core-io mp-units::core-fmt mp-units::systems mp-units::utility
)
add_library(mp-units::mp-units ALIAS mp-units)
install(TARGETS mp-units EXPORT mp-unitsTargets)
+1 -1
View File
@@ -65,5 +65,5 @@ function(add_units_module name)
add_library(mp-units::${name} ALIAS mp-units-${name})
install(TARGETS mp-units-${name} EXPORT mp-unitsTargets)
install(DIRECTORY include/units TYPE INCLUDE)
install(DIRECTORY include/mp-units TYPE INCLUDE)
endfunction()
+1 -1
View File
@@ -25,7 +25,7 @@ cmake_minimum_required(VERSION 3.19)
option(${projectPrefix}USE_LIBFMT "Enables usage of libfmt instead of the one from 'std'" ON)
message(STATUS "${projectPrefix}USE_LIBFMT: ${${projectPrefix}USE_LIBFMT}")
add_units_module(core-fmt DEPENDENCIES mp-units::core HEADERS include/units/format.h)
add_units_module(core-fmt DEPENDENCIES mp-units::core HEADERS include/mp-units/format.h)
target_compile_definitions(mp-units-core-fmt INTERFACE ${projectPrefix}USE_LIBFMT=$<BOOL:${${projectPrefix}USE_LIBFMT}>)
if(${projectPrefix}USE_LIBFMT)
@@ -28,14 +28,14 @@
// For the license information refer to format.h.
#include <gsl/gsl-lite.hpp>
#include <units/bits/fmt_hacks.h>
#include <mp-units/bits/fmt_hacks.h>
#include <concepts>
#include <limits>
#include <string_view>
// most of the below code is based on/copied from libfmt
namespace units::detail {
namespace mp_units::detail {
struct auto_id {};
@@ -382,11 +382,11 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
if (begin == end) return begin;
begin = ::units::detail::parse_align(begin, end, handler);
begin = ::mp_units::detail::parse_align(begin, end, handler);
if (begin == end) return begin;
// Parse sign.
begin = ::units::detail::parse_sign(begin, end, handler);
begin = ::mp_units::detail::parse_sign(begin, end, handler);
if (begin == end) return begin;
if (*begin == '#') {
@@ -400,12 +400,12 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
if (++begin == end) return begin;
}
begin = ::units::detail::parse_width(begin, end, handler);
begin = ::mp_units::detail::parse_width(begin, end, handler);
if (begin == end) return begin;
// Parse precision.
if (*begin == '.') {
begin = ::units::detail::parse_precision(begin, end, handler);
begin = ::mp_units::detail::parse_precision(begin, end, handler);
if (begin == end) return begin;
}
@@ -465,4 +465,4 @@ private:
ParseContext& context_;
};
} // namespace units::detail
} // namespace mp_units::detail
@@ -27,7 +27,7 @@
//
// For the license information refer to format.h.
#include <units/bits/external/hacks.h>
#include <mp-units/bits/external/hacks.h>
#ifndef UNITS_USE_LIBFMT
#define UNITS_USE_LIBFMT 1
@@ -22,16 +22,13 @@
#pragma once
#include <units/bits/fmt.h>
#include <units/customization_points.h>
#include <units/quantity.h>
#include <algorithm>
#include <mp-units/bits/algorithm.h>
#include <mp-units/bits/fmt.h>
#include <mp-units/customization_points.h>
#include <mp-units/quantity.h>
#include <mp-units/unit.h>
#include <cstdint>
// IWYU pragma: begin_exports
#include <units/bits/unit_text.h>
// IWYU pragma: end_exports
// Grammar
//
// units-format-spec ::= [fill-and-align] [width] [units-specs]
@@ -42,39 +39,14 @@
// conversion-spec ::= '%' units-type
// units-type ::= [units-rep-modifier] 'Q'
// [units-unit-modifier] 'q'
// one of "nt%"
// units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]
// units-rep-type ::= one of "aAbBdeEfFgGoxX"
// units-unit-modifier ::= 'A'
// units-unit-modifier ::= [units-text-encoding, units-unit-symbol-denominator, units-unit-symbol-separator]
// units-text-encoding ::= one of "UA"
// units-unit-symbol-solidus ::= one of "oan"
// units-unit-symbol-separator ::= one of "sd"
// Guide for editing
//
// If you want to add a new `units-type` terminal character (e.g. 'Q', 'q'):
// - If needed, write a new `specs` class (e.g. `global_format_specs`)
// - Add the new symbol in the `units_types` variable in the `parse_units_format` function
// - Add a new case in the `if` following the format_error in `parse_units_format` function;
// this should invoke `handler.on_[...]`
// - Edit `UNITS_STD_FMT::formatter`:
// - Add a new field for the flag/specs
// - Add to the `UNITS_STD_FMT::formatter::spec_handler` a `on_[...]` function that set the flag/specs if needed
// - Edit `quantity_formatter`:
// - Add a new field for the flag/specs
// - write a `on_[...]` function that writes to the `out` iterator the correct output
//
// If you want to add a new `units-rep-type`:
// - Add the new symbol in the `valid_rep_types` variable (which is in the
// UNITS_STD_FMT::formatter::spec_handler::on_type member function)
// NB: currently this function forward the modifier to the value that must be formatted;
// if the symbol has no meaning for UNITS_STD_FMT::formatter<Rep>, this behavior should be disabled manually
// (as is done for '\0')
// - Implement the effect of the new flag in `format_units_quantity_value`
//
// If you want to add a new `units-unit-modifier`:
// - Add the new symbol in the `valid_modifiers` variable (which is in the
// UNITS_STD_FMT::formatter::spec_handler::on_unit_modifier member function)
// - Implement the effect of the new flag in the `quantity_formatter::on_quantity_unit` member function
namespace units::detail {
namespace mp_units::detail {
// Holds specs about the whole object
template<typename CharT>
@@ -96,9 +68,7 @@ struct quantity_rep_format_specs {
};
// Holds specs about the unit (%[specs]q)
struct quantity_unit_format_specs {
bool ascii_only = false;
};
struct quantity_unit_format_specs : unit_symbol_formatting {};
template<typename CharT>
struct quantity_format_specs {
@@ -109,7 +79,7 @@ struct quantity_format_specs {
// Parse a `units-rep-modifier`
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
constexpr const It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_floating_point)
constexpr It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_floating_point)
{
// parse sign
begin = parse_sign(begin, end, handler);
@@ -126,7 +96,7 @@ constexpr const It parse_units_rep(It begin, S end, Handler&& handler, bool trea
if (treat_as_floating_point) {
begin = parse_precision(begin, end, handler);
} else
UNITS_THROW(UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation"));
throw UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation");
if (begin == end) return begin;
}
@@ -156,35 +126,18 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler)
}
if (begin != ptr) handler.on_text(begin, ptr);
begin = ++ptr; // consume '%'
if (ptr == end) UNITS_THROW(UNITS_STD_FMT::format_error("invalid format"));
if (ptr == end) throw UNITS_STD_FMT::format_error("invalid format");
c = *ptr++;
switch (c) {
// units-type
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
default:
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = std::find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) UNITS_THROW(UNITS_STD_FMT::format_error("invalid format"));
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(*begin); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) throw UNITS_STD_FMT::format_error("invalid format");
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(begin, new_end); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
begin = ptr;
}
if (begin != ptr) handler.on_text(begin, ptr);
@@ -264,14 +217,14 @@ OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<C
return UNITS_STD_FMT::format_to(out, "}}");
}
template<typename Dimension, typename Unit, typename Rep, typename Locale, typename CharT, typename OutputIt>
template<auto Reference, typename Rep, typename Locale, typename CharT, typename OutputIt>
struct quantity_formatter {
OutputIt out;
Rep val;
const quantity_format_specs<CharT>& specs;
Locale loc;
explicit quantity_formatter(OutputIt o, quantity<Dimension, Unit, Rep> q, const quantity_format_specs<CharT>& fspecs,
explicit quantity_formatter(OutputIt o, quantity<Reference, Rep> q, const quantity_format_specs<CharT>& fspecs,
Locale lc) :
out(o), val(std::move(q).number()), specs(fspecs), loc(std::move(lc))
{
@@ -289,28 +242,34 @@ struct quantity_formatter {
out = format_units_quantity_value<CharT>(out, val, specs.rep, loc);
}
void on_quantity_unit([[maybe_unused]] CharT)
template<std::input_iterator It, std::sentinel_for<It> S>
void on_quantity_unit(It, S)
{
auto txt = unit_text<Dimension, Unit>();
if (specs.unit.ascii_only) {
UNITS_STD_FMT::format_to(out, "{}", txt.ascii().c_str());
} else {
UNITS_STD_FMT::format_to(out, "{}", txt.standard().c_str());
}
out = unit_symbol_to<CharT>(out, get_unit(Reference), specs.unit);
}
};
} // namespace units::detail
template<std::input_iterator It, std::sentinel_for<It> S>
[[nodiscard]] constexpr It at_most_one_of(It begin, S end, std::string_view modifiers)
{
auto it = find_first_of(begin, end, modifiers.begin(), modifiers.end());
if (it != end && find_first_of(it + 1, end, modifiers.begin(), modifiers.end()) != end)
throw UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec");
return it;
}
template<typename Dimension, typename Unit, typename Rep, typename CharT>
struct UNITS_STD_FMT::formatter<units::quantity<Dimension, Unit, Rep>, CharT> {
} // namespace mp_units::detail
template<auto Reference, typename Rep, typename CharT>
struct UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> {
private:
using quantity = units::quantity<Dimension, Unit, Rep>;
using quantity = mp_units::quantity<Reference, Rep>;
using iterator = TYPENAME UNITS_STD_FMT::basic_format_parse_context<CharT>::iterator;
bool quantity_value = false;
bool quantity_unit = false;
units::detail::quantity_format_specs<CharT> specs;
mp_units::detail::quantity_format_specs<CharT> specs;
std::basic_string_view<CharT> format_str;
struct spec_handler {
@@ -318,9 +277,9 @@ private:
UNITS_STD_FMT::basic_format_parse_context<CharT>& context;
constexpr void on_fill(std::basic_string_view<CharT> fill) { f.specs.global.fill = fill; }
constexpr void on_align(units::detail::fmt_align align) { f.specs.global.align = align; }
constexpr void on_align(mp_units::detail::fmt_align align) { f.specs.global.align = align; }
constexpr void on_width(int width) { f.specs.global.width = width; }
constexpr void on_sign(units::detail::fmt_sign sign) { f.specs.rep.sign = sign; }
constexpr void on_sign(mp_units::detail::fmt_sign sign) { f.specs.rep.sign = sign; }
constexpr void on_hash() { f.specs.rep.alt = true; }
constexpr void on_precision(int precision) { f.specs.rep.precision = precision; }
constexpr void on_localized() { f.specs.rep.localized = true; }
@@ -331,30 +290,20 @@ private:
if (valid_rep_types.find(type) != std::string_view::npos) {
f.specs.rep.type = type;
} else {
UNITS_THROW(UNITS_STD_FMT::format_error("invalid quantity type specifier"));
}
}
constexpr void on_unit_modifier(char mod)
{
constexpr auto valid_modifiers = std::string_view{"A"};
if (valid_modifiers.find(mod) != std::string_view::npos) {
f.specs.unit.ascii_only = true;
} else {
UNITS_THROW(UNITS_STD_FMT::format_error("invalid unit modifier specified"));
throw UNITS_STD_FMT::format_error("invalid quantity type specifier");
}
}
template<typename T>
constexpr void on_dynamic_width(T t)
{
f.specs.global.dynamic_width_index = units::detail::on_dynamic_arg(t, context);
f.specs.global.dynamic_width_index = mp_units::detail::on_dynamic_arg(t, context);
}
template<typename T>
constexpr void on_dynamic_precision(T t)
{
f.specs.rep.dynamic_precision_index = units::detail::on_dynamic_arg(t, context);
f.specs.rep.dynamic_precision_index = mp_units::detail::on_dynamic_arg(t, context);
}
template<std::input_iterator It, std::sentinel_for<It> S>
@@ -365,13 +314,47 @@ private:
template<std::input_iterator It, std::sentinel_for<It> S>
constexpr void on_quantity_value(It begin, S end)
{
if (begin != end) units::detail::parse_units_rep(begin, end, *this, units::treat_as_floating_point<Rep>);
if (begin != end) mp_units::detail::parse_units_rep(begin, end, *this, mp_units::treat_as_floating_point<Rep>);
f.quantity_value = true;
}
constexpr void on_quantity_unit(CharT mod)
template<std::input_iterator It, std::sentinel_for<It> S>
constexpr void on_quantity_unit(It begin, S end)
{
if (mod != 'q') on_unit_modifier(mod);
if (begin == end) return;
constexpr auto valid_modifiers = std::string_view{"UAoansd"};
for (auto it = begin; it != end; ++it) {
if (valid_modifiers.find(*it) == std::string_view::npos)
throw UNITS_STD_FMT::format_error("invalid unit modifier specified");
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "UA"); it != end) {
if (*it == 'U')
f.specs.unit.encoding = mp_units::text_encoding::unicode;
else
f.specs.unit.encoding = mp_units::text_encoding::ascii;
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "oan"); it != end) {
if (*it == 'o')
f.specs.unit.solidus = mp_units::unit_symbol_solidus::one_denominator;
else if (*it == 'a')
f.specs.unit.solidus = mp_units::unit_symbol_solidus::always;
else
f.specs.unit.solidus = mp_units::unit_symbol_solidus::never;
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "sd"); it != end) {
if (*it == 's')
f.specs.unit.separator = mp_units::unit_symbol_separator::space;
else {
if (f.specs.unit.encoding == mp_units::text_encoding::ascii)
throw UNITS_STD_FMT::format_error("dot unit separator allowed only for Unicode encoding");
f.specs.unit.separator = mp_units::unit_symbol_separator::dot;
}
}
f.quantity_unit = true;
}
};
@@ -387,19 +370,19 @@ private:
spec_handler handler{*this, ctx};
// parse alignment
begin = units::detail::parse_align(begin, end, handler);
begin = mp_units::detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
// parse width
begin = units::detail::parse_width(begin, end, handler);
begin = mp_units::detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
// parse units-specific specification
end = units::detail::parse_units_format(begin, end, handler);
end = mp_units::detail::parse_units_format(begin, end, handler);
if (specs.global.align == units::detail::fmt_align::none && (!quantity_unit || quantity_value))
if (specs.global.align == mp_units::detail::fmt_align::none && (!quantity_unit || quantity_value))
// quantity values should behave like numbers (by default aligned to right)
specs.global.align = units::detail::fmt_align::right;
specs.global.align = mp_units::detail::fmt_align::right;
return {begin, end};
}
@@ -412,16 +395,15 @@ private:
if (begin == end || *begin == '}') {
// default format should print value followed by the unit separated with 1 space
out = units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
constexpr auto symbol = units::detail::unit_text<Dimension, Unit>();
if constexpr (symbol.standard().size() > 0) {
out = mp_units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
if constexpr (mp_units::detail::has_unit_symbol(get_unit(Reference))) {
*out++ = CharT(' ');
UNITS_STD_FMT::format_to(out, "{}", symbol.standard().c_str());
out = unit_symbol_to<CharT>(out, get_unit(Reference));
}
} else {
// user provided format
units::detail::quantity_formatter f(out, q, specs, ctx.locale());
units::detail::parse_units_format(begin, end, f);
mp_units::detail::quantity_formatter f(out, q, specs, ctx.locale());
mp_units::detail::parse_units_format(begin, end, f);
}
return out;
}
@@ -441,10 +423,10 @@ public:
// process dynamic width and precision
if (specs.global.dynamic_width_index >= 0)
specs.global.width =
units::detail::get_dynamic_spec<units::detail::width_checker>(specs.global.dynamic_width_index, ctx);
mp_units::detail::get_dynamic_spec<mp_units::detail::width_checker>(specs.global.dynamic_width_index, ctx);
if (specs.rep.dynamic_precision_index >= 0)
specs.rep.precision =
units::detail::get_dynamic_spec<units::detail::precision_checker>(specs.rep.dynamic_precision_index, ctx);
mp_units::detail::get_dynamic_spec<mp_units::detail::precision_checker>(specs.rep.dynamic_precision_index, ctx);
if (specs.global.width == 0) {
// Avoid extra copying if width is not specified
@@ -461,7 +443,7 @@ public:
// In `global_format_buffer` we will create a global format string
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}"
std::basic_string<CharT> global_format_buffer;
units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global);
mp_units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global);
// Format the `quantity buffer` using specs from `global_format_buffer`
// In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m")
+1 -1
View File
@@ -22,4 +22,4 @@
cmake_minimum_required(VERSION 3.19)
add_units_module(core-io DEPENDENCIES mp-units::core HEADERS include/units/quantity_io.h)
add_units_module(core-io DEPENDENCIES mp-units::core HEADERS include/mp-units/iostream.h)
@@ -23,32 +23,28 @@
#pragma once
#include <units/quantity.h>
// IWYU pragma: begin_exports
#include <units/bits/external/fixed_string_io.h>
#include <units/bits/unit_text.h>
#include <mp-units/quantity.h>
#include <mp-units/unit.h>
#include <sstream>
// IWYU pragma: end_exports
namespace units {
namespace mp_units {
namespace detail {
template<typename CharT, class Traits, typename D, typename U, typename Rep>
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
template<typename CharT, class Traits, auto R, typename Rep>
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
{
os << q.number();
constexpr auto symbol = detail::unit_text<D, U>();
if constexpr (!symbol.standard().empty()) {
os << " " << symbol.standard();
if constexpr (has_unit_symbol(get_unit(R))) {
os << " ";
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
}
}
} // namespace detail
template<typename CharT, typename Traits, typename D, typename U, typename Rep>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
template<typename CharT, typename Traits, auto R, typename Rep>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
requires requires { os << q.number(); }
{
if (os.width()) {
@@ -65,4 +61,4 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
return os;
}
} // namespace units
} // namespace mp_units
@@ -1,37 +0,0 @@
// 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 <units/bits/external/fixed_string.h>
#include <ostream>
namespace units {
template<typename CharT, class Traits, std::size_t N>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const basic_fixed_string<CharT, N>& txt)
{
return os << txt.c_str();
}
} // namespace units
+36 -43
View File
@@ -22,10 +22,6 @@
cmake_minimum_required(VERSION 3.19)
# core library options
set(${projectPrefix}DOWNCAST_MODE ON CACHE STRING "Select downcasting mode")
set_property(CACHE ${projectPrefix}DOWNCAST_MODE PROPERTY STRINGS AUTO ON OFF)
# find dependencies
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite CONFIG REQUIRED)
@@ -39,30 +35,41 @@ check_libcxx_in_use(${projectPrefix}LIBCXX)
add_library(
mp-units-core
INTERFACE
include/units/base_dimension.h
include/units/chrono.h
include/units/concepts.h
include/units/customization_points.h
include/units/derived_dimension.h
include/units/exponent.h
include/units/generic/angle.h
include/units/generic/dimensionless.h
include/units/generic/solid_angle.h
include/units/kind.h
include/units/magnitude.h
include/units/math.h
include/units/point_origin.h
include/units/prefix.h
include/units/quantity.h
include/units/quantity_cast.h
include/units/quantity_kind.h
include/units/quantity_point.h
include/units/quantity_point_kind.h
include/units/random.h
include/units/ratio.h
include/units/reference.h
include/units/symbol_text.h
include/units/unit.h
include/mp-units/bits/external/fixed_string.h
include/mp-units/bits/external/hacks.h
include/mp-units/bits/external/type_list.h
include/mp-units/bits/external/type_name.h
include/mp-units/bits/external/type_traits.h
include/mp-units/bits/algorithm.h
include/mp-units/bits/dimension_concepts.h
include/mp-units/bits/expression_template.h
include/mp-units/bits/get_associated_quantity.h
include/mp-units/bits/get_common_base.h
include/mp-units/bits/magnitude.h
include/mp-units/bits/math_concepts.h
include/mp-units/bits/prime.h
include/mp-units/bits/quantity_cast.h
include/mp-units/bits/quantity_concepts.h
include/mp-units/bits/quantity_point_concepts.h
include/mp-units/bits/quantity_spec_concepts.h
include/mp-units/bits/ratio.h
include/mp-units/bits/reference_concepts.h
include/mp-units/bits/representation_concepts.h
include/mp-units/bits/sudo_cast.h
include/mp-units/bits/symbol_text.h
include/mp-units/bits/text_tools.h
include/mp-units/bits/unit_concepts.h
include/mp-units/bits/value_cast.h
include/mp-units/concepts.h
include/mp-units/core.h
include/mp-units/customization_points.h
include/mp-units/dimension.h
include/mp-units/quantity.h
include/mp-units/quantity_point.h
include/mp-units/quantity_spec.h
include/mp-units/reference.h
include/mp-units/system_reference.h
include/mp-units/unit.h
)
target_compile_features(mp-units-core INTERFACE cxx_std_20)
target_link_libraries(mp-units-core INTERFACE gsl::gsl-lite)
@@ -85,23 +92,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
)
endif()
if(DEFINED ${projectPrefix}DOWNCAST_MODE)
set(downcast_mode_options OFF ON AUTO)
list(FIND downcast_mode_options "${${projectPrefix}DOWNCAST_MODE}" downcast_mode)
if(downcast_mode EQUAL -1)
message(FATAL_ERROR
"'${projectPrefix}DOWNCAST_MODE' should be one of ${downcast_mode_options} ('${${projectPrefix}DOWNCAST_MODE}' received)"
)
else()
message(STATUS "${projectPrefix}DOWNCAST_MODE: ${${projectPrefix}DOWNCAST_MODE}")
target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}DOWNCAST_MODE=${downcast_mode})
endif()
endif()
set_target_properties(mp-units-core PROPERTIES EXPORT_NAME core)
add_library(mp-units::core ALIAS mp-units-core)
# installation
install(TARGETS mp-units-core EXPORT mp-unitsTargets)
install(DIRECTORY include/units TYPE INCLUDE)
install(DIRECTORY include/mp-units TYPE INCLUDE)
@@ -22,11 +22,13 @@
#pragma once
#include <units/bits/external/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
#include <compare>
#include <initializer_list>
#include <iterator>
#include <ranges>
namespace units::detail {
namespace mp_units::detail {
// TODO refactor two below functions with std::ranges when moved to modules
@@ -59,6 +61,19 @@ constexpr auto get_first_of(const Rng& rng, UnaryFunction f)
}
// TODO remove all the below and use std when moved to modules
template<class InputIt, class ForwardIt>
constexpr InputIt find_first_of(InputIt first, InputIt last, ForwardIt s_first, ForwardIt s_last)
{
for (; first != last; ++first) {
for (ForwardIt it = s_first; it != s_last; ++it) {
if (*first == *it) {
return first;
}
}
}
return last;
}
template<class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{
@@ -94,7 +109,68 @@ constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2, Cmp
template<class I1, class I2>
constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2)
{
return ::units::detail::lexicographical_compare_three_way(f1, l1, f2, l2, std::compare_three_way());
return ::mp_units::detail::lexicographical_compare_three_way(f1, l1, f2, l2, std::compare_three_way());
}
} // namespace units::detail
template<class ForwardIt>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
{
if (first == last) return last;
ForwardIt largest = first;
++first;
for (; first != last; ++first)
if (*largest < *first) largest = first;
return largest;
}
template<class T>
constexpr T max(std::initializer_list<T> ilist)
{
return *max_element(ilist.begin(), ilist.end());
}
template<class I, class O>
struct in_out_result {
[[no_unique_address]] I in;
[[no_unique_address]] O out;
template<class I2, class O2>
requires std::convertible_to<const I&, I2> && std::convertible_to<const O&, O2>
constexpr operator in_out_result<I2, O2>() const&
{
return {in, out};
}
template<class I2, class O2>
requires std::convertible_to<I, I2> && std::convertible_to<O, O2>
constexpr operator in_out_result<I2, O2>() &&
{
return {std::move(in), std::move(out)};
}
};
template<class I, class O>
using copy_result = in_out_result<I, O>;
template<std::input_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O>
requires std::indirectly_copyable<I, O>
constexpr copy_result<I, O> copy(I first, S last, O result)
{
for (; first != last; ++first, (void)++result) {
*result = *first;
}
return {std::move(first), std::move(result)};
}
template<std::ranges::input_range R, std::weakly_incrementable O>
requires std::indirectly_copyable<std::ranges::iterator_t<R>, O>
constexpr copy_result<std::ranges::borrowed_iterator_t<R>, O> copy(R&& r, O result)
{
return ::mp_units::detail::copy(std::ranges::begin(r), std::ranges::end(r), std::move(result));
}
} // namespace mp_units::detail
@@ -0,0 +1,104 @@
// 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/expression_template.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/symbol_text.h>
namespace mp_units {
template<basic_symbol_text Symbol>
struct base_dimension;
namespace detail {
template<basic_symbol_text Symbol>
void to_base_specialization_of_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_base_dimension =
requires(T* t) { to_base_specialization_of_base_dimension(t); };
template<typename T>
inline constexpr bool is_specialization_of_base_dimension = false;
template<basic_symbol_text Symbol>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
/**
* @brief A concept matching all named base dimensions in the library.
*
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension =
is_derived_from_specialization_of_base_dimension<T> && (!is_specialization_of_base_dimension<T>);
template<typename T>
struct is_dimension_one : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_dim = requires {
requires is_specialization_of_power<T> &&
(BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_dims = false;
template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts>::value || is_power_of_dim<Ts>));
template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || is_dimension_one<T>::value || is_power_of_dim<T> || is_per_of_dims<T>;
} // namespace detail
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension;
namespace detail {
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types either being a specialization of `derived_dimension`
* or derived from it (inheritance needed to properly handle `dimension_one`).
*/
template<typename T>
concept DerivedDimension = is_derived_from_specialization_of<T, derived_dimension>;
} // namespace detail
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
} // namespace mp_units
@@ -0,0 +1,557 @@
// 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/external/type_list.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/math_concepts.h>
#include <mp-units/bits/ratio.h>
namespace mp_units {
/**
* @brief Type list type used by the expression template framework
*
* @tparam Ts The list of types
*/
template<typename... Ts>
struct type_list {};
/**
* @brief Type list type storing the list of components with negative exponents
*
* @note Can't be empty
*/
template<typename T, typename... Ts>
struct per {};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_per = false;
template<typename... Ts>
inline constexpr bool is_specialization_of_per<per<Ts...>> = true;
template<int Num, int... Den>
inline constexpr bool valid_ratio = true;
template<int... Den>
inline constexpr bool valid_ratio<0, Den...> = false;
template<int Num>
inline constexpr bool valid_ratio<Num, 0> = false;
template<>
inline constexpr bool valid_ratio<0, 0> = false;
template<int Num, int... Den>
inline constexpr bool positive_ratio = gt_zero<Num>;
template<int Num, int Den>
inline constexpr bool positive_ratio<Num, Den> = gt_zero<Num * Den>;
template<int Num, int... Den>
inline constexpr bool ratio_one = false;
template<>
inline constexpr bool ratio_one<1> = true;
template<int N>
inline constexpr bool ratio_one<N, N> = true;
} // namespace detail
/**
* @brief Type container for exponents with ratio different than `1`
*
* Ratio must be mathematically valid and non-negative. Negative exponents are passed to
* `per<...>` with inverted sign (as positive exponents).
*
* @tparam F factor to be raised to specified power
* @tparam Num Power ratio numerator
* @tparam Den [optional] Power ration denominator
*
* @note @p Den is an optional parameter to shorten the types presented to the user in the case when @p Den equals `1`.
*/
template<typename F, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && detail::positive_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power {
using factor = F;
static constexpr ratio exponent{Num, Den...};
};
namespace detail {
template<typename T>
struct expr_type_impl : std::type_identity<T> {};
template<typename T, int... Ints>
struct expr_type_impl<power<T, Ints...>> : std::type_identity<T> {};
} // namespace detail
template<typename T>
using expr_type = TYPENAME detail::expr_type_impl<T>::type;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power = false;
template<typename F, int... Ints>
inline constexpr bool is_specialization_of_power<power<F, Ints...>> = true;
template<typename T, ratio R>
consteval auto power_or_T_impl()
{
if constexpr (is_specialization_of_power<T>) {
return power_or_T_impl<typename T::factor, T::exponent * R>();
} else {
if constexpr (R.den == 1) {
if constexpr (R.num == 1)
return T{};
else
return power<T, R.num>{};
} else {
return power<T, R.num, R.den>{};
}
}
}
template<typename T, auto R>
// template<typename T, ratio R> // TODO ICE gcc 12
using power_or_T = decltype(power_or_T_impl<T, R>());
/**
* @brief Consolidates contiguous ranges of exponents of the same type
*
* If there is more than one exponent with the same type they are aggregated into one type by adding
* their powers.
*/
template<typename List>
struct expr_consolidate_impl;
template<>
struct expr_consolidate_impl<type_list<>> {
using type = type_list<>;
};
template<typename T>
struct expr_consolidate_impl<type_list<T>> {
using type = type_list<T>;
};
template<typename T, typename... Rest>
struct expr_consolidate_impl<type_list<T, Rest...>> {
using type = type_list_push_front<typename expr_consolidate_impl<type_list<Rest...>>::type, T>;
};
// replaces two instances of a type with one having the power of `2`
template<typename T, typename... Rest>
requires(!is_specialization_of_power<T>)
struct expr_consolidate_impl<type_list<T, T, Rest...>> {
using type = TYPENAME expr_consolidate_impl<type_list<power<T, 2>, Rest...>>::type;
};
// replaces the instance of a type and a power of it with one with incremented power
template<typename T, int... Ints, typename... Rest>
struct expr_consolidate_impl<type_list<T, power<T, Ints...>, Rest...>> {
using type = TYPENAME expr_consolidate_impl<type_list<power_or_T<T, power<T, Ints...>::exponent + 1>, Rest...>>::type;
};
// accumulates the powers of instances of the same type (removes the element in case the accumulation result is `0`)
template<typename T, int... Ints1, int... Ints2, typename... Rest>
struct expr_consolidate_impl<type_list<power<T, Ints1...>, power<T, Ints2...>, Rest...>> {
static constexpr ratio r = power<T, Ints1...>::exponent + power<T, Ints2...>::exponent;
using type = TYPENAME expr_consolidate_impl<type_list<power_or_T<T, r>, Rest...>>::type;
};
template<typename List>
using expr_consolidate = TYPENAME expr_consolidate_impl<List>::type;
/**
* @brief Simplifies the expression template
*
* Analyzes provided numerator and denominator type lists and simplifies elements with the same type.
* If the same power exists in both list, this elements gets omitted. Otherwise, the power of its
* exponent gets subtracted according to numerator and denominator elements powers.
*
* @tparam NumList type list for expression numerator
* @tparam DenList type list for expression denominator
* @tparam Pred predicate to be used for elements comparisons
*/
template<typename NumList, typename DenList, template<typename, typename> typename Pred>
struct expr_simplify;
// when one of the lists is empty there is nothing to do
template<typename NumList, typename DenList, template<typename, typename> typename Pred>
requires(type_list_size<NumList> == 0) || (type_list_size<DenList> == 0)
struct expr_simplify<NumList, DenList, Pred> {
using num = NumList;
using den = DenList;
};
// in case when front elements are different progress to the next element
template<typename Num, typename... NRest, typename Den, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<Num, NRest...>, type_list<Den, DRest...>, Pred> {
using impl = conditional<Pred<Num, Den>::value, expr_simplify<type_list<NRest...>, type_list<Den, DRest...>, Pred>,
expr_simplify<type_list<Num, NRest...>, type_list<DRest...>, Pred>>;
using num = conditional<Pred<Num, Den>::value, type_list_push_front<typename impl::num, Num>, typename impl::num>;
using den = conditional<Pred<Num, Den>::value, typename impl::den, type_list_push_front<typename impl::den, Den>>;
};
// in case two elements are of the same power such element gets omitted
template<typename T, typename... NRest, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<T, NRest...>, type_list<T, DRest...>, Pred> :
expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred> {};
template<typename T, ratio Num, ratio Den>
struct expr_simplify_power {
static constexpr ratio r = Num - Den;
using type = power_or_T<T, ratio{abs(r.num), r.den}>;
using num = conditional<(r > 0), type_list<type>, type_list<>>;
using den = conditional<(r < 0), type_list<type>, type_list<>>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, int... Ints, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<power<T, Ints...>, NRest...>, type_list<T, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints...>::exponent, ratio{1}>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, typename... DRest, int... Ints, template<typename, typename> typename Pred>
struct expr_simplify<type_list<T, NRest...>, type_list<power<T, Ints...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, ratio{1}, power<T, Ints...>::exponent>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, int... Ints1, typename... DRest, int... Ints2,
template<typename, typename> typename Pred>
requires(!std::same_as<power<T, Ints1...>, power<T, Ints2...>>)
struct expr_simplify<type_list<power<T, Ints1...>, NRest...>, type_list<power<T, Ints2...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints1...>::exponent, power<T, Ints2...>::exponent>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// expr_less
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
struct expr_less_impl : Pred<expr_type<Lhs>, expr_type<Rhs>> {};
template<typename T, int... Ints, template<typename, typename> typename Pred>
struct expr_less_impl<T, power<T, Ints...>, Pred> : std::true_type {};
/**
* @brief Compares two types with a given predicate
*
* Algorithm accounts not only for explicit types but also for the case when they
* are wrapped within `power<T, Num, Den>`.
*/
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
using expr_less = expr_less_impl<Lhs, Rhs, Pred>;
// expr_fractions
template<typename Num = type_list<>, typename Den = type_list<>>
struct expr_fractions_result {
using _num_ = Num; // exposition only
using _den_ = Den; // exposition only
};
template<template<typename> typename OneType, typename List>
[[nodiscard]] consteval auto expr_fractions_impl()
{
constexpr std::size_t size = type_list_size<List>;
if constexpr (size == 0)
return expr_fractions_result<>{};
else if constexpr (size == 1)
return expr_fractions_result<List>{};
else {
using last_element = type_list_back<List>;
if constexpr (is_specialization_of_per<last_element>) {
if constexpr (size == 2 && OneType<type_list_front<List>>::value)
return expr_fractions_result<type_list<>, type_list_map<last_element, type_list>>{};
else {
using split = type_list_split<List, size - 1>;
return expr_fractions_result<typename split::first_list, type_list_map<last_element, type_list>>{};
}
} else {
return expr_fractions_result<List>{};
}
}
}
/**
* @brief Divides expression template spec to numerator and denominator parts
*/
template<template<typename> typename OneType, typename... Ts>
struct expr_fractions : decltype(expr_fractions_impl<OneType, type_list<Ts...>>()) {};
// expr_make_spec
template<typename NumList, typename DenList, typename OneType, template<typename...> typename To>
[[nodiscard]] consteval auto expr_make_spec_impl()
{
constexpr std::size_t num = type_list_size<NumList>;
constexpr std::size_t den = type_list_size<DenList>;
if constexpr (num == 0 && den == 0) {
return OneType{};
} else if constexpr (num > 0 && den > 0) {
return type_list_map<type_list_push_back<NumList, type_list_map<DenList, per>>, To>{};
} else if constexpr (den > 0) {
return To<OneType, type_list_map<DenList, per>>{};
} else {
if constexpr (num == 1 && !is_specialization_of_power<type_list_front<NumList>>)
// temporary derived type not needed -> just return the original one
return type_list_front<NumList>{};
else
return type_list_map<NumList, To>{};
}
}
/**
* @brief Creates an expression template spec based on provided numerator and denominator parts
*/
template<typename NumList, typename DenList, typename OneType, template<typename...> typename To>
using expr_make_spec = decltype(expr_make_spec_impl<NumList, DenList, OneType, To>());
template<typename NumList, typename DenList, typename OneType, template<typename, typename> typename Pred,
template<typename...> typename To>
[[nodiscard]] consteval auto get_optimized_expression()
{
using num_list = expr_consolidate<NumList>;
using den_list = expr_consolidate<DenList>;
using simple = expr_simplify<num_list, den_list, Pred>;
using expr = expr_make_spec<typename simple::num, typename simple::den, OneType, To>;
return expr{};
}
/**
* @brief Multiplies two sorted expression template specs
*
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam Lhs lhs of the operation
* @tparam Rhs rhs of the operation
*/
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred, typename Lhs,
typename Rhs>
[[nodiscard]] consteval auto expr_multiply(Lhs, Rhs)
{
if constexpr (is_same_v<Lhs, OneType>) {
return Rhs{};
} else if constexpr (is_same_v<Rhs, OneType>) {
return Lhs{};
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived dimensions
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_num_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_den_, Pred>, OneType,
Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, type_list<Rhs>, Pred>,
typename Lhs::_den_, OneType, Pred, To>();
} else if constexpr (is_specialization_of<Rhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Rhs::_num_, type_list<Lhs>, Pred>,
typename Rhs::_den_, OneType, Pred, To>();
} else {
// two base dimensions
return get_optimized_expression<type_list_merge_sorted<type_list<Lhs>, type_list<Rhs>, Pred>, type_list<>, OneType,
Pred, To>();
}
}
/**
* @brief Divides two sorted expression template specs
*
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam Lhs lhs of the operation
* @tparam Rhs rhs of the operation
*/
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred, typename Lhs,
typename Rhs>
[[nodiscard]] consteval auto expr_divide(Lhs lhs, Rhs rhs)
{
if constexpr (is_same_v<Lhs, Rhs>) {
return OneType{};
} else if constexpr (is_same_v<Rhs, OneType>) {
return lhs;
} else if constexpr (is_same_v<Lhs, OneType>) {
return expr_divide<To, OneType, Pred>(To<>{}, rhs);
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived entities
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_den_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_num_, Pred>, OneType,
Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) {
return get_optimized_expression<
typename Lhs::_num_, type_list_merge_sorted<typename Lhs::_den_, type_list<Rhs>, Pred>, OneType, Pred, To>();
} else if constexpr (is_specialization_of<Rhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Rhs::_den_, type_list<Lhs>, Pred>,
typename Rhs::_num_, OneType, Pred, To>();
} else {
// two named entities
return To<Lhs, per<Rhs>>{};
}
}
/**
* @brief Inverts the expression template spec
*
* @tparam T expression template spec to invert
* @tparam OneType type that represents the value `1`
* @tparam To destination type list to put the result to
*/
template<template<typename...> typename To, typename OneType, typename T>
[[nodiscard]] consteval auto expr_invert(T)
{
if constexpr (is_specialization_of<T, To>)
return expr_make_spec<typename T::_den_, typename T::_num_, OneType, To>{};
else
return To<OneType, per<T>>{};
}
template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename... Nums, typename... Dens>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow_impl(type_list<Nums...>, type_list<Dens...>)
{
return detail::get_optimized_expression<type_list<power_or_T<Nums, ratio{Num, Den}>...>,
type_list<power_or_T<Dens, ratio{Num, Den}>...>, OneType, Pred, To>();
}
/**
* @brief Computes the value of an expression raised to the `Num/Den` power
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam T Expression being the base of the operation
*/
template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename T>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow(T)
{
return expr_pow_impl<Num, Den, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
}
// expr_map
template<typename T, template<typename> typename Proj>
struct expr_type_map;
template<typename T, template<typename> typename Proj>
requires requires { typename Proj<T>; }
struct expr_type_map<T, Proj> {
using type = Proj<T>;
};
template<typename F, int... Ints, template<typename> typename Proj>
requires requires { typename Proj<F>; }
struct expr_type_map<power<F, Ints...>, Proj> {
using type = power<Proj<F>, Ints...>;
};
template<typename T, template<typename> typename Proj>
concept expr_type_projectable = (requires { typename Proj<T>; } ||
(is_specialization_of_power<T> && requires { typename Proj<typename T::factor>; }));
template<typename T, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl = false;
template<typename... Ts, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl<type_list<Ts...>, Proj> = (... && expr_type_projectable<Ts, Proj>);
template<typename T, template<typename> typename Proj>
concept expr_projectable = requires {
typename T::_num_;
typename T::_den_;
requires expr_projectable_impl<typename T::_num_, Proj>;
requires expr_projectable_impl<typename T::_den_, Proj>;
};
template<typename T>
[[nodiscard]] consteval auto map_power(T t)
{
return t;
}
template<typename T, auto... Ints>
[[nodiscard]] consteval auto map_power(power<T, Ints...>)
{
return pow<Ints...>(T{});
}
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, expr_type_projectable<Proj>... Nums,
expr_type_projectable<Proj>... Dens>
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>)
{
return (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Nums>, Proj>::type{})) /
(OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Dens>, Proj>::type{}));
}
/**
* @brief Maps contents of one expression template to another resulting in a different type list
*
* @tparam Proj Projection to be used for mapping
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam T expression template to map from
*/
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, expr_projectable<Proj> T>
[[nodiscard]] consteval auto expr_map(T)
{
if constexpr (type_list_size<typename T::_num_> + type_list_size<typename T::_den_> == 0)
return OneType{};
else
return expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
}
} // namespace detail
} // namespace mp_units
@@ -22,9 +22,9 @@
#pragma once
#include <units/bits/external/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
#include <units/bits/algorithm.h>
#include <mp-units/bits/algorithm.h>
// IWYU pragma: begin_exports
#include <compare>
@@ -34,7 +34,7 @@
#include <cstddef>
namespace units {
namespace mp_units {
/**
* @brief A compile-time fixed string
@@ -111,4 +111,4 @@ basic_fixed_string(CharT) -> basic_fixed_string<CharT, 1>;
template<std::size_t N>
using fixed_string = basic_fixed_string<char, N>;
} // namespace units
} // namespace mp_units
@@ -52,6 +52,7 @@
#define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE("-Wshadow")
#define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE
#define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT UNITS_DIAGNOSTIC_IGNORE("-Wzero-as-nullpointer-constant")
#define UNITS_DIAGNOSTIC_IGNORE_DEPRECATED UNITS_DIAGNOSTIC_IGNORE("-Wdeprecated-declarations")
#else
#define UNITS_DIAGNOSTIC_PUSH UNITS_PRAGMA(warning(push))
#define UNITS_DIAGNOSTIC_POP UNITS_PRAGMA(warning(pop))
@@ -65,6 +66,7 @@
#define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE(4459)
#define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE UNITS_DIAGNOSTIC_IGNORE(4702)
#define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
#define UNITS_DIAGNOSTIC_IGNORE_DEPRECATED
#endif
#if _LIBCPP_VERSION
@@ -22,12 +22,14 @@
#pragma once
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
#include <cstddef>
#include <utility>
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_EXPR_ALWAYS_TF
namespace units {
namespace mp_units {
namespace detail {
@@ -42,6 +44,94 @@ inline constexpr bool is_type_list<T<Types...>> = true;
template<typename T>
concept TypeList = detail::is_type_list<T>;
// size
namespace detail {
template<typename List>
struct type_list_size_impl;
template<template<typename...> typename List, typename... Types>
struct type_list_size_impl<List<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> {};
} // namespace detail
template<TypeList List>
inline constexpr std::size_t type_list_size = detail::type_list_size_impl<List>::value;
// map
namespace detail {
template<typename T, template<typename...> typename To>
struct type_list_map_impl;
template<template<typename...> typename From, template<typename...> typename To, typename... Args>
struct type_list_map_impl<From<Args...>, To> {
using type = To<Args...>;
};
} // namespace detail
template<TypeList From, template<typename...> typename To>
using type_list_map = TYPENAME detail::type_list_map_impl<From, To>::type;
// element
namespace detail {
template<std::size_t I, typename T>
struct type_list_leaf {
using type = T;
};
template<typename Seq, typename...>
struct indexed_type_list_impl;
template<std::size_t... Is, typename... Ts>
struct indexed_type_list_impl<std::index_sequence<Is...>, Ts...> : type_list_leaf<Is, Ts>... {};
template<std::size_t I, typename T>
std::type_identity<T> type_list_element_func(const type_list_leaf<I, T>&);
} // namespace detail
template<typename... Ts>
struct indexed_type_list : detail::indexed_type_list_impl<std::index_sequence_for<Ts...>, Ts...> {};
template<TypeList List, std::size_t I>
using type_list_element_indexed = typename decltype(detail::type_list_element_func<I>(std::declval<List>()))::type;
template<TypeList List, std::size_t I>
using type_list_element = type_list_element_indexed<type_list_map<List, indexed_type_list>, I>;
// front
namespace detail {
template<typename List>
struct type_list_front_impl;
template<template<typename...> typename List, typename T, typename... Ts>
struct type_list_front_impl<List<T, Ts...>> {
using type = T;
};
} // namespace detail
template<TypeList List>
using type_list_front = TYPENAME detail::type_list_front_impl<List>::type;
// back
template<TypeList List>
using type_list_back = type_list_element<List, type_list_size<List> - 1>;
// push_front
namespace detail {
@@ -59,6 +149,7 @@ struct type_list_push_front_impl<List<OldTypes...>, NewTypes...> {
template<TypeList List, typename... Types>
using type_list_push_front = TYPENAME detail::type_list_push_front_impl<List, Types...>::type;
// push_back
namespace detail {
@@ -76,6 +167,7 @@ struct type_list_push_back_impl<List<OldTypes...>, NewTypes...> {
template<TypeList List, typename... Types>
using type_list_push_back = TYPENAME detail::type_list_push_back_impl<List, Types...>::type;
// join
namespace detail {
@@ -95,46 +187,29 @@ struct type_list_join_impl<List<First...>, List<Second...>, Rest...> {
template<TypeList... Lists>
using type_list_join = TYPENAME detail::type_list_join_impl<Lists...>::type;
// split
namespace detail {
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename... Types>
struct split_impl;
template<typename List, typename First, typename Second>
struct type_list_split_impl;
template<template<typename...> typename List, std::size_t Idx, std::size_t N>
struct split_impl<List, Idx, N> {
using first_list = List<>;
using second_list = List<>;
};
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
requires(Idx < N)
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME type_list_push_front_impl<typename base::first_list, T>::type;
using second_list = TYPENAME base::second_list;
};
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME base::first_list;
using second_list = TYPENAME type_list_push_front_impl<typename base::second_list, T>::type;
template<template<typename...> typename List, typename... Args, std::size_t... First, std::size_t... Second>
struct type_list_split_impl<List<Args...>, std::index_sequence<First...>, std::index_sequence<Second...>> {
using indexed_list = indexed_type_list<Args...>;
using first_list = List<type_list_element_indexed<indexed_list, First>...>;
using second_list = List<type_list_element_indexed<indexed_list, sizeof...(First) + Second>...>;
};
} // namespace detail
template<TypeList List, std::size_t N>
struct type_list_split;
requires(N <= type_list_size<List>)
struct type_list_split :
detail::type_list_split_impl<List, std::make_index_sequence<N>,
std::make_index_sequence<type_list_size<List> - N>> {};
template<template<typename...> typename List, std::size_t N, typename... Types>
struct type_list_split<List<Types...>, N> {
static_assert(N <= sizeof...(Types), "Invalid index provided");
using split = detail::split_impl<List, 0, N, Types...>;
using first_list = TYPENAME split::first_list;
using second_list = TYPENAME split::second_list;
};
// split_half
@@ -144,6 +219,7 @@ struct type_list_split_half;
template<template<typename...> typename List, typename... Types>
struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {};
// merge_sorted
namespace detail {
@@ -166,7 +242,6 @@ struct type_list_merge_sorted_impl<List<>, List<Rhs...>, Pred> {
using type = List<Rhs...>;
};
template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest,
template<typename, typename> typename Pred>
requires Pred<Lhs1, Rhs1>::value
@@ -187,6 +262,7 @@ struct type_list_merge_sorted_impl<List<Lhs1, LhsRest...>, List<Rhs1, RhsRest...
template<TypeList SortedList1, TypeList SortedList2, template<typename, typename> typename Pred>
using type_list_merge_sorted = TYPENAME detail::type_list_merge_sorted_impl<SortedList1, SortedList2, Pred>::type;
// sort
namespace detail {
@@ -218,6 +294,6 @@ struct type_list_sort_impl<List<Types...>, Pred> {
template<TypeList List, template<typename, typename> typename Pred>
using type_list_sort = TYPENAME detail::type_list_sort_impl<List, Pred>::type;
} // namespace units
} // namespace mp_units
UNITS_DIAGNOSTIC_POP
+40
View File
@@ -0,0 +1,40 @@
// https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
#pragma once
#include <string_view>
template<typename T>
[[nodiscard]] consteval std::string_view type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
template<typename T1, typename T2>
[[nodiscard]] consteval auto better_type_name(T1 v1, T2 v2)
{
if constexpr (type_name<T1>().size() < type_name<T2>().size())
return v1;
else if constexpr (type_name<T2>().size() < type_name<T1>().size())
return v2;
else if constexpr (type_name<T1>() < type_name<T2>())
return v1;
else
return v2;
}
@@ -22,11 +22,11 @@
#pragma once
#include <units/bits/external/hacks.h>
#include <mp-units/bits/external/hacks.h>
#include <type_traits>
#include <utility>
namespace units {
namespace mp_units {
// conditional
namespace detail {
@@ -65,6 +65,12 @@ inline constexpr bool is_specialization_of = false;
template<typename... Params, template<typename...> typename Type>
inline constexpr bool is_specialization_of<Type<Params...>, Type> = true;
template<typename T, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v = false;
template<auto... Params, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v<Type<Params...>, Type> = true;
// is_derived_from_specialization_of
namespace detail {
@@ -77,4 +83,72 @@ template<typename T, template<typename...> typename Type>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
} // namespace units
template<typename T, typename... Ts>
concept one_of = (false || ... || std::same_as<T, Ts>);
template<typename T, auto... Vs>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_same_v<std::remove_const_t<decltype(Vs)>, T>);
}
template<template<typename...> typename T, typename... Ts>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_specialization_of<Ts, T>);
}
template<template<auto...> typename T, typename... Ts>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_specialization_of_v<Ts, T>);
}
template<typename T, std::same_as<T> auto V>
[[nodiscard]] consteval auto get()
{
return V;
}
template<typename T, auto V1, auto V2, auto... Vs>
[[nodiscard]] consteval auto get()
{
if constexpr (is_same_v<T, std::remove_const_t<decltype(V1)>>)
return V1;
else
return get<T, V2, Vs...>();
}
template<template<typename...> typename T, typename T1>
requires is_specialization_of<T1, T>
[[nodiscard]] consteval auto get()
{
return T1{};
}
template<template<typename...> typename T, typename T1, typename T2, typename... Ts>
[[nodiscard]] consteval auto get()
{
if constexpr (is_specialization_of<T1, T>)
return T1{};
else
return get<T, T2, Ts...>();
}
template<template<auto...> typename T, typename T1>
requires is_specialization_of_v<T1, T>
[[nodiscard]] consteval auto get()
{
return T1{};
}
template<template<auto...> typename T, typename T1, typename T2, typename... Ts>
[[nodiscard]] consteval auto get()
{
if constexpr (is_specialization_of_v<T1, T>)
return T1{};
else
return get<T, T2, Ts...>();
}
} // namespace mp_units
@@ -0,0 +1,86 @@
// 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/expression_template.h>
#include <mp-units/bits/unit_concepts.h>
#include <mp-units/quantity_spec.h>
namespace mp_units::detail {
template<AssociatedUnit U>
[[nodiscard]] consteval auto all_are_kinds(U);
template<typename U, auto... Vs>
[[nodiscard]] consteval auto all_are_kinds(power<U, Vs...>)
{
return all_are_kinds(U{});
}
template<typename... Nums, typename... Dens>
[[nodiscard]] consteval bool all_are_kinds(type_list<Nums...>, type_list<Dens...>)
{
return (... && all_are_kinds(Nums{})) && (... && all_are_kinds(Dens{}));
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto all_are_kinds(U)
{
if constexpr (requires { U::quantity_spec; })
return QuantityKindSpec<std::remove_const_t<decltype(U::quantity_spec)>>;
else if constexpr (requires { U::reference_unit; })
return all_are_kinds(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) {
return all_are_kinds(typename U::_num_{}, typename U::_den_{});
}
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u);
template<AssociatedUnit U>
using to_quantity_spec = std::remove_const_t<decltype(get_associated_quantity_impl(U{}))>;
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u)
{
if constexpr (requires { U::quantity_spec; })
return remove_kind(U::quantity_spec);
else if constexpr (requires { U::reference_unit; })
return get_associated_quantity_impl(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) {
return expr_map<to_quantity_spec, derived_quantity_spec, struct dimensionless, type_list_of_quantity_spec_less>(u);
}
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U u)
{
constexpr bool all_kinds = all_are_kinds(u);
if constexpr (all_kinds)
return kind_of<get_associated_quantity_impl(u)>;
else
return get_associated_quantity_impl(u);
}
} // namespace mp_units::detail
@@ -0,0 +1,91 @@
// 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/quantity_spec_concepts.h>
namespace mp_units::detail {
[[nodiscard]] consteval std::size_t hierarchy_path_length(QuantitySpec auto q)
{
if constexpr (requires { q._parent_; })
return hierarchy_path_length(q._parent_) + 1;
else
return 1;
}
template<std::size_t Offset>
requires(Offset >= 0)
[[nodiscard]] consteval QuantitySpec auto hierarchy_path_advance(QuantitySpec auto q)
{
if constexpr (Offset == 0)
return q;
else if constexpr (requires { q._parent_; })
return hierarchy_path_advance<Offset - 1>(q._parent_);
}
template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval bool have_common_base_in_hierarchy_of_equal_length(A a, B b)
{
if constexpr (is_same_v<A, B>)
return true;
else if constexpr (requires { a._parent_; })
return have_common_base_in_hierarchy_of_equal_length(a._parent_, b._parent_);
else
return false;
}
template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval auto have_common_base(A a, B b)
{
constexpr int a_length = hierarchy_path_length(A{});
constexpr int b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return have_common_base_in_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
}
template<QuantitySpec A, QuantitySpec B>
requires(have_common_base_in_hierarchy_of_equal_length(A{}, B{}))
[[nodiscard]] consteval auto get_common_base_for_hierarchy_of_equal_length(A a, B b)
{
if constexpr (is_same_v<A, B>)
return a;
else
return get_common_base_for_hierarchy_of_equal_length(a._parent_, b._parent_);
}
template<QuantitySpec A, QuantitySpec B>
requires(have_common_base(A{}, B{}))
[[nodiscard]] consteval auto get_common_base(A a, B b)
{
constexpr int a_length = hierarchy_path_length(A{});
constexpr int b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return get_common_base_for_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
}
} // namespace mp_units::detail
@@ -22,23 +22,74 @@
#pragma once
#include <units/bits/external/hacks.h>
#include <units/bits/prime.h>
#include <units/ratio.h>
#include <mp-units/bits/expression_template.h>
#include <mp-units/bits/external/hacks.h>
#include <mp-units/bits/external/type_name.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/math_concepts.h>
#include <mp-units/bits/prime.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/symbol_text.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/customization_points.h>
#include <concepts>
#include <cstdint>
#include <exception>
#include <numbers>
#include <optional>
namespace units {
namespace mp_units {
namespace detail {
// Higher numbers use fewer trial divisions, at the price of more storage space.
using factorizer = wheel_factorizer<4>;
} // namespace detail
namespace detail {
template<typename T>
inline constexpr bool is_magnitude = false;
template<typename T>
inline constexpr bool is_specialization_of_magnitude = false;
} // namespace detail
/**
* @brief Any type which can be used as a basis vector in a BasePower.
* @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Magnitude = detail::is_magnitude<T>;
/**
* @brief A type to represent a standalone constant value.
*/
// template<auto V>
// struct constant {
// static constexpr auto value = V;
// };
// // is_derived_from_specialization_of_constant
// namespace detail {
// template<auto V>
// void to_base_specialization_of_constant(const volatile constant<V>*);
// template<typename T>
// inline constexpr bool is_derived_from_specialization_of_constant =
// requires(T * t) { to_base_specialization_of_constant(t); };
// } // namespace detail
// template<typename T>
// concept Constant = detail::is_derived_from_specialization_of_constant<T>;
// struct pi_v : constant<std::numbers::pi_v<long double>> {};
/**
* @brief Any type which can be used as a basis vector in a PowerV.
*
* We have two categories.
*
@@ -52,9 +103,6 @@ using factorizer = wheel_factorizer<4>;
* The reason we can't hold the value directly for floating point bases is so that we can support some compilers (e.g.,
* GCC 10) which don't yet permit floating point NTTPs.
*/
template<typename T>
concept BaseRep =
std::is_same_v<T, std::intmax_t> || std::is_same_v<std::remove_cvref_t<decltype(T::value)>, long double>;
/**
* @brief A basis vector in our magnitude representation, raised to some rational power.
@@ -80,72 +128,119 @@ concept BaseRep =
* _existing_ bases, including both prime numbers and any other irrational bases. For example, even though `sqrt(2)` is
* irrational, we must not ever use it as a base; instead, we would use `base_power{2, ratio{1, 2}}`.
*/
template<BaseRep T>
struct base_power {
// The rational power to which the base is raised.
ratio power{1};
constexpr long double get_base() const { return T::value; }
};
/**
* @brief Specialization for prime number bases.
*/
template<>
struct base_power<std::intmax_t> {
// The value of the basis "vector". Must be prime to be used with `magnitude` (below).
std::intmax_t base;
// The rational power to which the base is raised.
ratio power{1};
constexpr std::intmax_t get_base() const { return base; }
};
/**
* @brief Deduction guides for base_power: only permit deducing integral bases.
*/
template<std::integral T, std::convertible_to<ratio> U>
base_power(T, U) -> base_power<std::intmax_t>;
template<std::integral T>
base_power(T) -> base_power<std::intmax_t>;
// Implementation for BasePower concept (below).
namespace detail {
template<typename T>
inline constexpr bool is_base_power = false;
template<BaseRep T>
inline constexpr bool is_base_power<base_power<T>> = true;
inline constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
}
template<typename T>
concept PowerVBase = one_of<T, int, std::intmax_t, long double> || detail::is_named_magnitude<T>;
// TODO Unify with `power` if UTPs (P1985) are accepted by the Committee
template<PowerVBase auto V, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power_v {
static constexpr auto base = V;
static constexpr ratio exponent{Num, Den...};
};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power_v = false;
template<auto V, int... Ints>
inline constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
} // namespace detail
template<typename T>
concept MagnitudeSpec = PowerVBase<T> || detail::is_specialization_of_power_v<T>;
namespace detail {
template<MagnitudeSpec Element>
[[nodiscard]] consteval auto get_base(Element element)
{
if constexpr (detail::is_specialization_of_power_v<Element>)
return Element::base;
else
return element;
}
template<MagnitudeSpec Element>
[[nodiscard]] consteval ratio get_exponent(Element)
{
if constexpr (detail::is_specialization_of_power_v<Element>)
return Element::exponent;
else
return ratio{1};
}
} // namespace detail
/**
* @brief Concept to detect whether a _type_ is a valid base power.
*
* Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with
* `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template.
*/
template<typename T>
concept BasePower = detail::is_base_power<T>;
namespace detail {
constexpr auto inverse(BasePower auto bp)
// We do not want magnitude type to have the `l` literal after a value for a small integral number.
// For example this modifies `magnitude<3l>` to be `magnitude<3>`
template<auto V>
[[nodiscard]] consteval auto shorten_T()
{
bp.power.num *= -1;
return bp;
if constexpr (std::integral<decltype(V)>) {
if constexpr (V <= std::numeric_limits<int>::max()) {
return static_cast<int>(V);
} else {
return static_cast<std::intmax_t>(V);
}
} else if constexpr (std::floating_point<decltype(V)>) {
return static_cast<long double>(V);
} else {
return V;
}
}
template<PowerVBase auto V, ratio R>
[[nodiscard]] consteval auto power_v_or_T()
{
constexpr auto shortT = shorten_T<V>();
if constexpr (R.den == 1) {
if constexpr (R.num == 1)
return shortT;
else
return power_v<shortT, R.num>{};
} else {
return power_v<shortT, R.num, R.den>{};
}
}
template<MagnitudeSpec M>
[[nodiscard]] consteval auto inverse(M)
{
return power_v_or_T<get_base(M{}), -1 * get_exponent(M{})>();
}
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
template<typename T>
using widen_t =
std::conditional_t<std::is_arithmetic_v<T>,
std::conditional_t<std::is_floating_point_v<T>, long double,
std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
T>;
using widen_t = conditional<std::is_arithmetic_v<T>,
conditional<std::is_floating_point_v<T>, long double,
conditional<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
T>;
// Raise an arbitrary arithmetic type to a positive integer power at compile time.
template<typename T>
constexpr T int_power(T base, std::integral auto exp)
[[nodiscard]] consteval T int_power(T base, std::integral auto exp)
{
// As this function should only be called at compile time, the terminations herein function as
// "parameter-compatible static_asserts", and should not result in terminations at runtime.
@@ -182,27 +277,28 @@ constexpr T int_power(T base, std::integral auto exp)
template<typename T>
constexpr widen_t<T> compute_base_power(BasePower auto bp)
[[nodiscard]] consteval widen_t<T> compute_base_power(MagnitudeSpec auto el)
{
// This utility can only handle integer powers. To compute rational powers at compile time, we'll
// need to write a custom function.
//
// Note that since this function should only be called at compile time, the point of these
// terminations is to act as "static_assert substitutes", not to actually terminate at runtime.
if (bp.power.den != 1) {
const auto exp = get_exponent(el);
if (exp.den != 1) {
std::terminate(); // Rational powers not yet supported
}
if (bp.power.num < 0) {
if (exp.num < 0) {
if constexpr (std::is_integral_v<T>) {
std::terminate(); // Cannot represent reciprocal as integer
} else {
return T{1} / compute_base_power<T>(inverse(bp));
return T{1} / compute_base_power<T>(inverse(el));
}
}
auto power = bp.power.num;
return int_power(static_cast<widen_t<T>>(bp.get_base()), power);
auto power = exp.num;
return int_power(static_cast<widen_t<T>>(get_base(el)), power);
}
// A converter for the value member variable of magnitude (below).
@@ -212,7 +308,7 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
template<typename To, typename From>
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
requires(!std::is_integral_v<To> || std::is_integral_v<From>)
constexpr To checked_static_cast(From x)
[[nodiscard]] consteval To checked_static_cast(From x)
{
// This function should only ever be called at compile time. The purpose of these terminations is
// to produce compiler errors, because we cannot `static_assert` on function arguments.
@@ -224,31 +320,15 @@ constexpr To checked_static_cast(From x)
return static_cast<To>(x);
}
} // namespace detail
/**
* @brief Equality detection for two base powers.
*/
template<BasePower T, BasePower U>
constexpr bool operator==(T t, U u)
{
return std::is_same_v<T, U> && (t.get_base() == u.get_base()) && (t.power == u.power);
}
/**
* @brief A BasePower, raised to a rational power E.
*/
constexpr auto pow(BasePower auto bp, ratio p)
{
bp.power = bp.power * p;
return bp;
}
// A variety of implementation detail helpers.
namespace detail {
// The exponent of `factor` in the prime factorization of `n`.
constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
[[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
{
std::intmax_t m = 0;
while (n % factor == 0) {
@@ -261,7 +341,7 @@ constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
// Divide a number by a given base raised to some power.
//
// Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n.
constexpr std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n)
[[nodiscard]] consteval std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n)
{
while (pow-- > 0) {
n /= base;
@@ -270,181 +350,216 @@ constexpr std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std:
}
// A way to check whether a number is prime at compile time.
constexpr bool is_prime(std::intmax_t n) { return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n)); }
// [[nodiscard]] consteval bool is_prime(std::intmax_t n)
// {
// return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n));
// }
constexpr bool is_valid_base_power(const BasePower auto& bp)
{
if (bp.power == 0) {
return false;
}
// template<MagnitudeSpec Element>
// [[nodiscard]] consteval bool is_valid_element(Element element)
// {
// if (get_exponent(element) == 0) {
// return false;
// }
// if constexpr (std::integral<decltype(get_base(element))>) {
// // Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr
// steps
// // and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor`
// // workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being
// // usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a
// // template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter.
// (See
// // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// // distinction.)
// //
// // In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// // most likely and common mistakes.
// if (const bool too_big_to_check = (get_base_value(element) > 1'000'000'000)) {
// return true;
// }
if constexpr (std::is_same_v<decltype(bp.get_base()), std::intmax_t>) {
// Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr steps
// and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor`
// workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being
// usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a
// template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter. (See
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// distinction.)
//
// In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// most likely and common mistakes.
if (const bool too_big_to_check = (bp.get_base() > 1'000'000'000)) {
return true;
}
return is_prime(bp.get_base());
} else {
return bp.get_base() > 0;
}
}
// return is_prime(get_base_value(element));
// } else {
// return get_base_value(element) > 0;
// }
// }
// A function object to apply a predicate to all consecutive pairs of values in a sequence.
template<typename Predicate>
struct pairwise_all {
Predicate predicate;
// template<typename Predicate>
// struct pairwise_all {
// Predicate predicate;
template<typename... Ts>
constexpr bool operator()(Ts&&... ts) const
{
// Carefully handle different sizes, avoiding unsigned integer underflow.
constexpr auto num_comparisons = [](auto num_elements) {
return (num_elements > 1) ? (num_elements - 1) : 0;
}(sizeof...(Ts));
// template<typename... Ts>
// [[nodiscard]] consteval bool operator()(Ts&&... ts) const
// {
// // Carefully handle different sizes, avoiding unsigned integer underflow.
// constexpr auto num_comparisons = [](auto num_elements) {
// return (num_elements > 1) ? (num_elements - 1) : 0;
// }(sizeof...(Ts));
// Compare zero or more pairs of neighbours as needed.
return [this]<std::size_t... Is>(std::tuple<Ts...>&& t, std::index_sequence<Is...>) {
return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
}(std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
}
};
// // Compare zero or more pairs of neighbours as needed.
// return [this]<std::size_t... Is>(std::tuple<Ts...> && t, std::index_sequence<Is...>)
// {
// return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
// }
// (std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
// }
// };
// Deduction guide: permit constructions such as `pairwise_all{std::less{}}`.
template<typename T>
pairwise_all(T) -> pairwise_all<T>;
// template<typename T>
// pairwise_all(T) -> pairwise_all<T>;
// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing.
template<typename... Ts>
requires(std::is_signed_v<Ts> && ...)
constexpr bool strictly_increasing(Ts&&... ts)
// template<typename... Ts>
// requires(std::is_signed_v<Ts> && ...)
// [[nodiscard]] consteval bool strictly_increasing(Ts&&... ts)
// {
// return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
// }
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
[[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element)
{
return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return std::is_integral_v<decltype(get_base(element))> && (get_exponent(element).den == 1);
}
template<BasePower auto... BPs>
inline constexpr bool all_base_powers_valid = (is_valid_base_power(BPs) && ...);
template<BasePower auto... BPs>
inline constexpr bool all_bases_in_order = strictly_increasing(BPs.get_base()...);
template<BasePower auto... BPs>
inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> && all_bases_in_order<BPs...>;
constexpr bool is_rational(BasePower auto bp)
[[nodiscard]] consteval bool is_integral(MagnitudeSpec auto element)
{
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1);
static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return is_rational(element) && get_exponent(element).num > 0;
}
constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; }
} // namespace detail
/**
* @brief A representation for positive real numbers which optimizes taking products and rational powers.
*
* Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to
* rational powers, and compare for equality.
*/
template<BasePower auto... BPs>
requires detail::is_base_power_pack_valid<BPs...>
template<MagnitudeSpec auto... Ms>
// requires detail::is_element_pack_valid<Ms...>
struct magnitude {
// Whether this magnitude represents an integer.
friend constexpr bool is_integral(const magnitude&) { return (detail::is_integral(BPs) && ...); }
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
}
// Whether this magnitude represents a rational number.
friend constexpr bool is_rational(const magnitude&) { return (detail::is_rational(BPs) && ...); }
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
}
};
// Implementation for Magnitude concept (below).
namespace detail {
template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T>
inline constexpr bool is_magnitude = false;
template<auto... BPs>
inline constexpr bool is_magnitude<magnitude<BPs...>> = true;
inline constexpr bool is_derived_from_specialization_of_magnitude =
requires(T* t) { to_base_specialization_of_magnitude(t); };
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
/**
* @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Magnitude = detail::is_magnitude<T>;
/**
* @brief The value of a Magnitude in a desired type T.
*/
template<typename T, auto... BPs>
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
requires(!std::is_integral_v<T> || is_integral(magnitude<BPs...>{}))
constexpr T get_value(const magnitude<BPs...>&)
template<typename T, auto... Ms>
requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
constexpr T get_value(const magnitude<Ms...>&)
{
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(BPs) * ... * T{1}));
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(Ms) * ... * T{1}));
return result;
}
/**
* @brief A base to represent pi.
*/
struct pi_base {
static constexpr long double value = std::numbers::pi_v<long double>;
};
/**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/
inline constexpr Magnitude auto mag_pi = magnitude<base_power<pi_base>{}>{};
inline constexpr struct mag_pi : magnitude<std::numbers::pi_v<long double>> {
} mag_pi;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude equality implementation.
template<auto... LeftBPs, auto... RightBPs>
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>)
template<Magnitude M1, Magnitude M2>
[[nodiscard]] consteval bool operator==(M1, M2)
{
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) {
return ((LeftBPs == RightBPs) && ...);
} else {
return false;
}
return std::is_same_v<M1, M2>;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude rational powers implementation.
template<ratio E, auto... BPs>
constexpr auto pow(magnitude<BPs...>)
template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
[[nodiscard]] consteval auto pow(magnitude<Ms...>)
{
if constexpr (E.num == 0) {
if constexpr (Num == 0) {
return magnitude<>{};
} else {
return magnitude<pow(BPs, E)...>{};
return magnitude<detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * ratio{Num, Den}>()...>{};
}
}
template<auto... BPs>
constexpr auto sqrt(magnitude<BPs...> m)
template<auto... Ms>
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{
return pow<ratio{1, 2}>(m);
return pow<1, 2>(m);
}
template<auto... BPs>
constexpr auto cbrt(magnitude<BPs...> m)
template<auto... Ms>
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{
return pow<ratio{1, 3}>(m);
return pow<1, 3>(m);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude product implementation.
namespace detail {
consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs)
{
using lhs_base_t = decltype(get_base(lhs));
using rhs_base_t = decltype(get_base(rhs));
if constexpr (is_named_magnitude<lhs_base_t> && is_named_magnitude<rhs_base_t>)
return type_name<lhs_base_t>() < type_name<rhs_base_t>();
else if constexpr (!is_named_magnitude<lhs_base_t> && !is_named_magnitude<rhs_base_t>)
return get_base(lhs) < get_base(rhs);
else
return is_named_magnitude<lhs_base_t>;
}
} // namespace detail
// Base cases, for when either (or both) inputs are the identity.
constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; }
@@ -452,37 +567,38 @@ constexpr Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; }
// Recursive case for the product of any two non-identity Magnitudes.
template<auto H1, auto... T1, auto H2, auto... T2>
constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
[[nodiscard]] consteval Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
{
// Case for when H1 has the smaller base.
if constexpr (H1.get_base() < H2.get_base()) {
using namespace detail;
if constexpr (less(H1, H2)) {
if constexpr (sizeof...(T1) == 0) {
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
return magnitude<H1, H2, T2...>{};
} else {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
}
}
// Case for when H2 has the smaller base.
if constexpr (H1.get_base() > H2.get_base()) {
} else if constexpr (less(H2, H1)) {
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
}
} else {
if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
if constexpr (get_exponent(H1) + get_exponent(H2) == 0) {
return partial_product;
} else {
// Make a new power_v with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = power_v_or_T<get_base(H1), get_exponent(H1) + get_exponent(H2)>();
// "Same leading base" case.
if constexpr (H1.get_base() == H2.get_base()) {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
// Make a new base_power with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = [&](auto head) {
head.power = H1.power + H2.power;
return head;
}(H1);
if constexpr (new_head.power == 0) {
return partial_product;
if constexpr (get_exponent(new_head) == 0) {
return partial_product;
} else {
return magnitude<new_head>{} * partial_product;
}
}
} else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} else {
return magnitude<new_head>{} * partial_product;
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
}
}
}
@@ -490,7 +606,7 @@ constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude quotient implementation.
constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude numerator and denominator implementation.
@@ -498,19 +614,15 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1
namespace detail {
// The largest integer which can be extracted from any magnitude with only a single basis vector.
template<auto BP>
constexpr auto integer_part(magnitude<BP>)
template<auto M>
[[nodiscard]] consteval auto integer_part(magnitude<M>)
{
constexpr auto power_num = BP.power.num;
constexpr auto power_den = BP.power.den;
constexpr auto power_num = get_exponent(M).num;
constexpr auto power_den = get_exponent(M).den;
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
constexpr auto largest_integer_power = [=](BasePower auto bp) {
bp.power = (power_num / power_den); // Note: integer division intended.
return bp;
}(BP); // Note: lambda is immediately invoked.
return magnitude<largest_integer_power>{};
if constexpr (std::is_integral_v<decltype(get_base(M))> && (power_num >= power_den)) {
// largest integer power
return magnitude<power_v_or_T<get_base(M), power_num / power_den>()>{}; // Note: integer division intended
} else {
return magnitude<>{};
}
@@ -518,13 +630,13 @@ constexpr auto integer_part(magnitude<BP>)
} // namespace detail
template<auto... BPs>
constexpr auto numerator(magnitude<BPs...>)
template<auto... Ms>
[[nodiscard]] consteval auto numerator(magnitude<Ms...>)
{
return (detail::integer_part(magnitude<BPs>{}) * ... * magnitude<>{});
return (detail::integer_part(magnitude<Ms>{}) * ... * magnitude<>{});
}
constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
[[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
// Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`.
constexpr ratio as_ratio(Magnitude auto m)
@@ -556,44 +668,50 @@ constexpr ratio as_ratio(Magnitude auto m)
// minimum power for each base (where absent bases implicitly have a power of 0).
namespace detail {
template<auto BP>
constexpr auto remove_positive_power(magnitude<BP> m)
template<auto M>
[[nodiscard]] consteval auto remove_positive_power(magnitude<M> m)
{
if constexpr (BP.power.num < 0) {
if constexpr (get_exponent(M).num < 0) {
return m;
} else {
return magnitude<>{};
}
}
template<auto... BPs>
constexpr auto remove_positive_powers(magnitude<BPs...>)
template<auto... Ms>
[[nodiscard]] consteval auto remove_positive_powers(magnitude<Ms...>)
{
return (magnitude<>{} * ... * remove_positive_power(magnitude<BPs>{}));
return (magnitude<>{} * ... * remove_positive_power(magnitude<Ms>{}));
}
} // namespace detail
// Base cases, for when either (or both) inputs are the identity.
constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); }
constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); }
[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m)
{
return detail::remove_positive_powers(m);
}
[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>)
{
return detail::remove_positive_powers(m);
}
// Recursive case for the common Magnitude of any two non-identity Magnitudes.
template<auto H1, auto... T1, auto H2, auto... T2>
constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
[[nodiscard]] consteval auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
{
using detail::remove_positive_power;
if constexpr (H1.get_base() < H2.get_base()) {
if constexpr (detail::get_base(H1) < detail::get_base(H2)) {
// When H1 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
} else if constexpr (H2.get_base() < H1.get_base()) {
} else if constexpr (detail::get_base(H2) < detail::get_base(H1)) {
// When H2 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
} else {
// When the bases are equal, pick whichever has the lower power.
constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
if constexpr (H1.power < H2.power) {
if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) {
return magnitude<H1>{} * common_tail;
} else {
return magnitude<H2>{} * common_tail;
@@ -601,6 +719,18 @@ constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
}
}
template<auto... Ms>
[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude<Ms...>)
{
return (... * decltype(get_base(Ms)){}) * std::intmax_t{};
}
// Returns the most precise type to express the magnitude factor
template<Magnitude auto M>
using common_magnitude_type = decltype(common_magnitude_type_impl(M));
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// `mag()` implementation.
@@ -614,11 +744,12 @@ template<std::intmax_t N>
inline constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt;
namespace detail {
// Helper to perform prime factorization at compile time.
template<std::intmax_t N>
requires(N > 0)
requires gt_zero<N>
struct prime_factorization {
static constexpr std::intmax_t get_or_compute_first_factor()
[[nodiscard]] static consteval std::intmax_t get_or_compute_first_factor()
{
if constexpr (known_first_factor<N>.has_value()) {
return known_first_factor<N>.value();
@@ -632,7 +763,7 @@ struct prime_factorization {
static constexpr std::intmax_t remainder = remove_power(first_base, first_power, N);
static constexpr auto value =
magnitude<base_power{first_base, first_power}>{} * prime_factorization<remainder>::value;
magnitude<power_v_or_T<first_base, ratio{first_power}>()>{} * prime_factorization<remainder>::value;
};
// Specialization for the prime factorization of 1 (base case).
@@ -643,6 +774,7 @@ struct prime_factorization<1> {
template<std::intmax_t N>
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
} // namespace detail
/**
@@ -652,42 +784,74 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
* manually adding base powers.
*/
template<ratio R>
requires(R.num > 0)
constexpr Magnitude auto mag()
{
return detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
}
requires detail::gt_zero<R.num>
inline constexpr Magnitude auto mag = detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
/**
* @brief Create a Magnitude which is some rational number raised to a rational power.
*/
template<ratio Base, ratio Pow>
requires(Base.num > 0)
constexpr Magnitude auto mag_power()
{
return pow<Pow>(mag<Base>());
}
requires detail::gt_zero<Base.num>
inline constexpr Magnitude auto mag_power = pow<Pow.num, Pow.den>(mag<Base>);
namespace detail {
template<typename T, BasePower auto... BPs>
constexpr ratio get_power(T base, magnitude<BPs...>)
template<typename T, auto... Ms>
[[nodiscard]] consteval ratio get_power(T base, magnitude<Ms...>)
{
return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0});
return ((get_base(Ms) == base ? get_exponent(Ms) : ratio{0}) + ... + ratio{0});
}
constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; }
[[nodiscard]] consteval std::intmax_t integer_part(ratio r) { return r.num / r.den; }
constexpr std::intmax_t extract_power_of_10(Magnitude auto m)
[[nodiscard]] consteval std::intmax_t extract_power_of_10(Magnitude auto m)
{
const auto power_of_2 = get_power(2, m);
const auto power_of_5 = get_power(5, m);
if ((power_of_2 * power_of_5).num <= 0) {
return 0;
}
if ((power_of_2 * power_of_5).num <= 0) return 0;
return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5);
}
} // namespace detail
} // namespace units
inline constexpr basic_symbol_text base_multiplier("× 10", "x 10");
template<Magnitude auto M>
[[nodiscard]] consteval auto magnitude_text()
{
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / mag_power<10, exp10>;
constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base);
// TODO address the below
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
constexpr auto num_value = get_value<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den);
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>();
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
auto txt = basic_fixed_string("[") + regular<num_value>();
if constexpr (den_value == 1) {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("]");
} else {
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
}
} else {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
} else {
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
basic_fixed_string("]");
}
}
} else {
return basic_fixed_string("");
}
}
} // namespace detail
} // namespace mp_units
@@ -24,12 +24,12 @@
#include <cstdint>
namespace units::detail {
namespace mp_units::detail {
template<std::intmax_t N>
template<auto N>
concept gt_zero = (N > 0);
template<std::intmax_t N>
template<auto N>
concept non_zero = (N != 0);
} // namespace units::detail
} // namespace mp_units::detail
@@ -22,7 +22,7 @@
#pragma once
#include <units/bits/algorithm.h>
#include <mp-units/bits/algorithm.h>
#include <array>
#include <cstddef>
#include <cstdint>
@@ -30,9 +30,9 @@
#include <optional>
#include <tuple>
namespace units::detail {
namespace mp_units::detail {
constexpr bool is_prime_by_trial_division(std::uintmax_t n)
[[nodiscard]] consteval bool is_prime_by_trial_division(std::uintmax_t n)
{
for (std::uintmax_t f = 2; f * f <= n; f += 1 + (f % 2)) {
if (n % f == 0) {
@@ -45,7 +45,7 @@ constexpr bool is_prime_by_trial_division(std::uintmax_t n)
// Return the first factor of n, as long as it is either k or n.
//
// Precondition: no integer smaller than k evenly divides n.
constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std::uintmax_t k)
[[nodiscard]] constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std::uintmax_t k)
{
if (n % k == 0) {
return k;
@@ -57,7 +57,7 @@ constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std
}
template<std::size_t N>
constexpr std::array<std::uintmax_t, N> first_n_primes()
[[nodiscard]] consteval std::array<std::uintmax_t, N> first_n_primes()
{
std::array<std::uintmax_t, N> primes;
primes[0] = 2;
@@ -71,7 +71,7 @@ constexpr std::array<std::uintmax_t, N> first_n_primes()
}
template<std::size_t N, typename Callable>
constexpr void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis, Callable&& call)
consteval void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis, Callable&& call)
{
for (std::uintmax_t i = 0u; i < n; ++i) {
if (std::apply([&i](auto... primes) { return ((i % primes != 0) && ...); }, basis)) {
@@ -81,7 +81,7 @@ constexpr void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::u
}
template<std::size_t N>
constexpr std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis)
[[nodiscard]] consteval std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis)
{
std::size_t count = 0u;
call_for_coprimes_up_to(n, basis, [&count](auto) { ++count; });
@@ -89,7 +89,7 @@ constexpr std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std:
}
template<std::size_t ResultSize, std::size_t N>
constexpr auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>& basis)
[[nodiscard]] consteval auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>& basis)
{
std::array<std::uintmax_t, ResultSize> coprimes;
std::size_t i = 0u;
@@ -100,7 +100,7 @@ constexpr auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>
}
template<std::size_t N>
constexpr std::uintmax_t product(const std::array<std::uintmax_t, N>& values)
[[nodiscard]] consteval std::uintmax_t product(const std::array<std::uintmax_t, N>& values)
{
std::uintmax_t product = 1;
for (const auto& v : values) {
@@ -135,7 +135,7 @@ struct wheel_factorizer {
static constexpr auto coprimes_in_first_wheel =
coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis);
static constexpr std::uintmax_t find_first_factor(std::uintmax_t n)
[[nodiscard]] static consteval std::uintmax_t find_first_factor(std::uintmax_t n)
{
if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(n, p); })) return *k;
@@ -150,7 +150,7 @@ struct wheel_factorizer {
return n;
}
static constexpr bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
[[nodiscard]] static consteval bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
};
} // namespace units::detail
} // namespace mp_units::detail
@@ -22,29 +22,34 @@
#pragma once
// IWYU pragma: begin_exports
#include <units/symbol_text.h>
// IWYU pragma: end_exports
#include <mp-units/bits/quantity_concepts.h>
#include <mp-units/reference.h>
#include <type_traits>
#include <units/quantity_cast.h>
namespace units {
struct one : derived_unit<one> {};
struct percent : named_scaled_unit<percent, "%", mag<ratio(1, 100)>(), one> {};
namespace mp_units {
/**
* @brief Dimension one
* @brief Explicit cast of a quantity type
*
* Dimension for which all the exponents of the factors corresponding to the base
* dimensions are zero. Also commonly named as "dimensionless".
* This cast converts only a quantity type. It might be used to force some quantity type
* conversions that are not implicitly allowed but are allowed explicitly.
*
* For example:
*
* @code{.cpp}
* auto length = isq::length(42 * m);
* auto distance = quantity_cast<isq::distance>(length);
* @endcode
*
* @note This cast does not affect the underlying value of a number stored in a quantity.
*
* @tparam ToQS a quantity specification to use for a target quantity
*/
struct dim_one : derived_dimension<dim_one, one> {};
template<QuantitySpec auto ToQS, typename Q>
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
requires Quantity<std::remove_cvref_t<Q>> && (castable(q.quantity_spec, ToQS))
{
return make_quantity<reference<ToQS, q.unit>{}>(std::forward<Q>(q).number());
}
template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>;
template<UnitOf<dim_one> U, Representation Rep = double>
using dimensionless = quantity<dim_one, U, Rep>;
} // namespace units
} // namespace mp_units
@@ -0,0 +1,81 @@
// 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/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/customization_points.h>
namespace mp_units {
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
namespace detail {
template<auto R, typename Rep>
void to_base_specialization_of_quantity(const volatile quantity<R, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity =
requires(T* t) { to_base_specialization_of_quantity(t); };
} // namespace detail
/**
* @brief A concept matching all quantities in the library
*
* Satisfied by all types being a either specialization or derived from `quantity`
*/
template<typename T>
concept Quantity = detail::is_derived_from_specialization_of_quantity<T>;
/**
* @brief A concept matching all quantities with provided dimension or quantity spec
*
* Satisfied by all quantities with a dimension/quantity_spec being the instantiation derived from
* the provided dimension/quantity_spec type.
*/
template<typename Q, auto V>
concept QuantityOf = Quantity<Q> && ReferenceOf<std::remove_const_t<decltype(Q::reference)>, V>;
/**
* @brief A concept matching all external quantities like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_like_traits` provide
* all quantity-specific information.
*/
template<typename T>
concept QuantityLike = requires(T q) {
quantity_like_traits<T>::reference;
typename quantity_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_like_traits<T>::reference)>>;
requires RepresentationOf<typename quantity_like_traits<T>::rep,
get_quantity_spec(quantity_like_traits<T>::reference).character>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
} // namespace mp_units
@@ -0,0 +1,128 @@
// 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/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/customization_points.h>
namespace mp_units {
template<QuantitySpec auto Q>
struct absolute_point_origin;
namespace detail {
template<typename T>
inline constexpr bool is_quantity_point = false;
template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T* t) { to_base_specialization_of_absolute_point_origin(t); };
} // namespace detail
/**
* @brief A concept matching all quantity points in the library
*
* Satisfied by all types being a either specialization or derived from `quantity_point`
*/
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
/**
* @brief A concept matching all quantity point origins in the library
*
* Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template.
*/
template<typename T>
concept PointOrigin = QuantityPoint<T> || detail::is_derived_from_specialization_of_absolute_point_origin<T>;
/**
* @brief A concept matching all quantity point origins for a specified quantity type in the library
*
* Satisfied by all quantity point origins that are defined using a provided quantity specification.
*/
template<typename T, auto Q>
concept PointOriginFor =
PointOrigin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> && implicitly_convertible(Q, T::quantity_spec);
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity_point;
namespace detail {
template<auto R, auto PO, typename Rep>
void to_base_specialization_of_quantity_point(const volatile quantity_point<R, PO, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_point =
requires(T* t) { to_base_specialization_of_quantity_point(t); };
template<typename T>
requires is_derived_from_specialization_of_quantity_point<T>
inline constexpr bool is_quantity_point<T> = true;
} // namespace detail
/**
* @brief A concept matching all quantity points with provided dimension or quantity spec
*
* Satisfied by all quantity points with a dimension/quantity_spec being the instantiation derived from
* the provided dimension/quantity_spec type, or quantity points having the origin with the same
* `absolute_point_origin`.
*/
template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> &&
(ReferenceOf<std::remove_const_t<decltype(QP::reference)>, V> ||
(PointOrigin<std::remove_const_t<decltype(V)>> &&
std::convertible_to<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>>));
/**
* @brief A concept matching all external quantity point like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_point_like_traits` provide
* all quantity_point-specific information.
*/
template<typename T>
concept QuantityPointLike = requires(T q) {
quantity_point_like_traits<T>::reference;
quantity_point_like_traits<T>::point_origin;
typename quantity_point_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_point_like_traits<T>::reference)>>;
requires PointOrigin<std::remove_const_t<decltype(quantity_point_like_traits<T>::point_origin)>>;
requires RepresentationOf<typename quantity_point_like_traits<T>::rep,
get_quantity_spec(quantity_point_like_traits<T>::reference).character>;
requires std::constructible_from<
typename quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin,
typename quantity_point_like_traits<T>::rep>::quantity_type,
decltype(quantity_point_like_traits<T>::relative(q))>;
};
} // namespace mp_units
@@ -0,0 +1,167 @@
// 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/dimension_concepts.h>
#include <mp-units/bits/expression_template.h>
namespace mp_units {
#ifdef __cpp_explicit_this_parameter
template<auto...>
#else
template<typename, auto...>
#endif
struct quantity_spec;
template<auto Q>
struct kind_of_;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_kind_of = false;
template<auto Q>
inline constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
template<typename T>
concept QuantityKindSpec = is_specialization_of_kind_of<T>;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else
template<typename T, auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Args...>*);
#endif
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_spec =
requires(T* t) { to_base_specialization_of_quantity_spec(t); };
#ifdef __cpp_explicit_this_parameter
template<BaseDimension auto Dim, auto... Args>
template<auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Dim, Args...>*);
#else
template<typename Self, BaseDimension auto Dim, auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Self, Dim, Args...>*);
#endif
template<typename T>
inline constexpr bool is_derived_from_specialization_of_base_quantity_spec =
requires(T* t) { to_base_specialization_of_base_quantity_spec(t); };
template<typename T>
inline constexpr bool is_specialization_of_quantity_spec = false;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else
template<typename T, auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
#endif
/**
* @brief Concept matching all named quantity specification types
*
* Satisfied by all types that derive from `quantity_spec`.
*/
template<typename T>
concept NamedQuantitySpec = is_derived_from_specialization_of_quantity_spec<T> &&
(!is_specialization_of_quantity_spec<T>)&&(!QuantityKindSpec<T>);
/**
* @brief Concept matching all named base quantity specification types
*
* Satisfied by all types that derive from `quantity_spec` taking a base dimension
* as a template parameter.
*/
template<typename T>
concept BaseQuantitySpec = NamedQuantitySpec<T> && is_derived_from_specialization_of_base_quantity_spec<T>;
template<typename T>
struct is_dimensionless : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_quantity_spec = requires {
requires is_specialization_of_power<T> &&
(NamedQuantitySpec<typename T::factor> || is_dimensionless<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_quantity_specs = false;
template<typename... Ts>
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts>::value || is_power_of_quantity_spec<Ts>));
template<typename T>
concept IntermediateDerivedQuantitySpecExpr =
detail::NamedQuantitySpec<T> || detail::is_dimensionless<T>::value || detail::is_power_of_quantity_spec<T> ||
detail::is_per_of_quantity_specs<T>;
} // namespace detail
template<detail::IntermediateDerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec;
namespace detail {
/**
* @brief Concept matching all derived quantity specification types
*
* Satisfied by all `derived_quantity_spec` specializations.
*
* @note Deriving a strong type from it is considered a logic error and thus is
* explicitly not supported here.
*/
template<typename T>
concept IntermediateDerivedQuantitySpec =
is_specialization_of<T, derived_quantity_spec> ||
(QuantityKindSpec<T> &&
is_specialization_of<std::remove_const_t<decltype(T::_quantity_spec_)>, derived_quantity_spec>);
} // namespace detail
template<typename T>
concept QuantitySpec =
detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T> || detail::QuantityKindSpec<T>;
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q);
namespace detail {
template<auto To, auto From>
concept DerivedFromQuantityKindSpecOf =
QuantitySpec<std::remove_const_t<decltype(From)>> && QuantitySpec<std::remove_const_t<decltype(To)>> &&
get_kind(From) != get_kind(To) &&
std::derived_from<std::remove_cvref_t<decltype(To)>, std::remove_cvref_t<decltype(get_kind(From))>>;
}
} // namespace mp_units
+121
View File
@@ -0,0 +1,121 @@
// 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 <gsl/gsl-lite.hpp>
#include <compare>
#include <cstdint>
#include <numeric>
namespace mp_units {
namespace detail {
template<typename T>
[[nodiscard]] consteval T abs(T v) noexcept
{
return v < 0 ? -v : v;
}
[[nodiscard]] consteval std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
{
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4);
const std::intmax_t a0 = abs(lhs) % c;
const std::intmax_t a1 = abs(lhs) / c;
const std::intmax_t b0 = abs(rhs) % c;
const std::intmax_t b1 = abs(rhs) / c;
gsl_Assert(a1 == 0 || b1 == 0); // overflow in multiplication
gsl_Assert(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
gsl_Assert(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
gsl_Assert((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
return lhs * rhs;
}
} // namespace detail
/**
* @brief Provides compile-time rational arithmetic support.
*
* This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class
* are used as class NTTPs rather then a type template parameter kind.
*/
struct ratio {
std::intmax_t num;
std::intmax_t den;
consteval explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num{n}, den{d}
{
gsl_Expects(den != 0);
if (num == 0)
den = 1;
else {
std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd;
}
}
[[nodiscard]] friend consteval bool operator==(ratio, ratio) = default;
[[nodiscard]] friend consteval auto operator<=>(ratio lhs, ratio rhs) { return (lhs - rhs).num <=> 0; }
[[nodiscard]] friend consteval ratio operator-(ratio r) { return ratio{-r.num, r.den}; }
[[nodiscard]] friend consteval ratio operator+(ratio lhs, ratio rhs)
{
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den};
}
[[nodiscard]] friend consteval ratio operator-(ratio lhs, ratio rhs) { return lhs + (-rhs); }
[[nodiscard]] friend consteval ratio operator*(ratio lhs, ratio rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio{detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1)};
}
[[nodiscard]] friend consteval ratio operator/(ratio lhs, ratio rhs) { return lhs * ratio{rhs.den, rhs.num}; }
};
[[nodiscard]] consteval bool is_integral(ratio r) { return r.num % r.den == 0; }
[[nodiscard]] consteval ratio common_ratio(ratio r1, ratio r2)
{
if (r1.num == r2.num && r1.den == r2.den) return ratio{r1.num, r1.den};
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r1.num > r2.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r2.num > r1.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r1.den > r2.den);
std::intmax_t num = std::gcd(r1.num * r2.den, r2.num * r1.den);
std::intmax_t den = r1.den * r2.den;
std::intmax_t gcd = std::gcd(num, den);
return ratio{num / gcd, den / gcd};
}
} // namespace mp_units
@@ -0,0 +1,82 @@
// 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/quantity_spec_concepts.h>
#include <mp-units/bits/unit_concepts.h>
namespace mp_units {
template<QuantitySpec auto Q, Unit auto U>
struct reference;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_reference = false;
template<auto Q, auto U>
inline constexpr bool is_specialization_of_reference<reference<Q, U>> = true;
} // namespace detail
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = AssociatedUnit<T> || detail::is_specialization_of_reference<T>;
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u);
template<auto Q, auto U>
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(reference<Q, U>)
{
return Q;
}
[[nodiscard]] consteval Unit auto get_unit(AssociatedUnit auto u) { return u; }
template<auto Q, auto U>
[[nodiscard]] consteval Unit auto get_unit(reference<Q, U>)
{
return U;
}
/**
* @brief A concept matching all references with provided dimension or quantity spec
*
* Satisfied by all references with a dimension/quantity_spec being the instantiation derived from
* the provided dimension/quantity_spec type.
*/
template<typename T, auto V>
concept ReferenceOf =
Reference<T> &&
((Dimension<std::remove_const_t<decltype(V)>> && get_quantity_spec(T{}).dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(get_quantity_spec(T{}), V) &&
!detail::DerivedFromQuantityKindSpecOf<get_quantity_spec(T{}), V> &&
(detail::QuantityKindSpec<std::remove_const_t<decltype(get_quantity_spec(T{}))>> ||
!detail::DerivedFromQuantityKindSpecOf<V, get_quantity_spec(T{})>)));
} // namespace mp_units
@@ -0,0 +1,85 @@
// 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/customization_points.h>
#include <concepts>
#include <cstdint>
#include <functional>
#include <type_traits>
namespace mp_units {
/**
* @brief Quantity character
*
* Scalars, vectors and tensors are mathematical objects that can be used to
* denote certain physical quantities and their values. They are as such
* independent of the particular choice of a coordinate system, whereas
* each scalar component of a vector or a tensor and each component vector and
* component tensor depend on that choice.
*
* A scalar is a physical quantity that has magnitude but no direction.
*
* Vectors are physical quantities that possess both magnitude and direction
* and whose operations obey the axioms of a vector space.
*
* Tensors can be used to describe more general physical quantities.
* For example, the Cauchy stress tensor possess magnitude, direction,
* and orientation qualities.
*/
enum class quantity_character { scalar, vector, tensor };
namespace detail {
template<typename T, typename U>
concept CommonTypeWith =
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> && std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept ScalableNumber =
std::regular_invocable<std::multiplies<>, T, U> && std::regular_invocable<std::divides<>, T, U>;
template<typename T>
concept CastableNumber = CommonTypeWith<T, std::intmax_t> && ScalableNumber<std::common_type_t<T, std::intmax_t>>;
// TODO Fix it according to sudo_cast implementation
template<typename T>
concept Scalable = CastableNumber<T> ||
(requires { typename T::value_type; } && CastableNumber<typename T::value_type> &&
ScalableNumber<T, std::common_type_t<typename T::value_type, std::intmax_t>>) ||
(requires { typename T::element_type; } && CastableNumber<typename T::element_type> &&
ScalableNumber<T, std::common_type_t<typename T::element_type, std::intmax_t>>);
} // namespace detail
template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && detail::Scalable<T>;
template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
} // namespace mp_units
@@ -0,0 +1,86 @@
// 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/external/type_traits.h>
#include <mp-units/bits/magnitude.h>
#include <mp-units/bits/quantity_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/unit.h>
namespace mp_units::detail {
// determines the best available representation type
template<Quantity From, Quantity To>
[[nodiscard]] consteval auto common_rep_type(From, To)
{
if constexpr (requires { typename std::common_type_t<typename From::rep, typename To::rep>; })
// returns a common type of two representation types if available
// i.e. `double` and `int` will end up with `double` precision
return std::common_type_t<typename From::rep, typename To::rep>{};
else
return typename From::rep{};
}
/**
* @brief Explicit cast between different quantity types
*
* @note This is a low-level facility and is too powerful to be used by the users directly. They should either use
* `value_cast` or `quantity_cast`.
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, typename From>
requires Quantity<std::remove_cvref_t<From>> &&
(castable(std::remove_reference_t<From>::quantity_spec, To::quantity_spec)) &&
((std::remove_reference_t<From>::unit == To::unit &&
std::constructible_from<typename To::rep, typename std::remove_reference_t<From>::rep>) ||
(std::remove_reference_t<From>::unit != To::unit)) // && scalable_with_<typename To::rep>))
// TODO how to constrain the second part here?
[[nodiscard]] constexpr Quantity auto sudo_cast(From&& q)
{
if constexpr (q.unit == To::unit) {
// no scaling of the number needed
return make_quantity<To::reference>(
static_cast<TYPENAME To::rep>(std::forward<From>(q).number())); // this is the only (and recommended) way to do
// a truncating conversion on a number, so we are
// using static_cast to suppress all the compiler
// warnings on conversions
} else {
// scale the number
constexpr Magnitude auto c_mag = get_canonical_unit(q.unit).mag / get_canonical_unit(To::unit).mag;
constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto irr = c_mag * (den / num);
using c_rep_type = decltype(common_rep_type(q, To{}));
using c_mag_type = common_magnitude_type<c_mag>;
using multiplier_type =
conditional<treat_as_floating_point<c_rep_type>, std::common_type_t<c_mag_type, long double>, c_mag_type>;
constexpr auto val = [](Magnitude auto m) { return get_value<multiplier_type>(m); };
return static_cast<TYPENAME To::rep>(static_cast<c_rep_type>(std::forward<From>(q).number()) * val(num) / val(den) *
val(irr)) *
To::reference;
}
}
} // namespace mp_units::detail
@@ -0,0 +1,188 @@
// 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
// IWYU pragma: begin_exports
#include <mp-units/bits/external/fixed_string.h>
#include <mp-units/bits/external/hacks.h>
#include <compare>
#include <cstddef>
#include <cstdint>
// IWYU pragma: end_exports
#include <gsl/gsl-lite.hpp>
namespace mp_units {
namespace detail {
constexpr void validate_ascii_char([[maybe_unused]] char c) noexcept { gsl_Expects((c & 0x80) == 0); }
template<std::size_t N>
constexpr void validate_ascii_string([[maybe_unused]] const char (&s)[N + 1]) noexcept
{
#ifndef NDEBUG
if constexpr (N != 0)
for (size_t i = 0; i < N; ++i) validate_ascii_char(s[i]);
#endif
}
} // namespace detail
/**
* @brief A symbol text representation
*
* This class template is responsible for definition and handling of a symbol text
* representation. In the libary it is used to define symbols of units and prefixes.
* Each symbol can have two versions: Unicode and ASCI-only.
*
* @tparam UnicodeCharT Character type to be used for a Unicode representation
* @tparam N The size of a Unicode symbol
* @tparam M The size of the ASCII-only symbol
*/
template<typename UnicodeCharT, std::size_t N, std::size_t M>
struct basic_symbol_text {
basic_fixed_string<UnicodeCharT, N> unicode_;
basic_fixed_string<char, M> ascii_;
constexpr explicit(false) basic_symbol_text(char txt) noexcept : unicode_(txt), ascii_(txt)
{
detail::validate_ascii_char(txt);
}
constexpr basic_symbol_text(UnicodeCharT u, char a) noexcept : unicode_(u), ascii_(a)
{
detail::validate_ascii_char(a);
}
constexpr explicit(false) basic_symbol_text(const char (&txt)[N + 1]) noexcept : unicode_(txt), ascii_(txt)
{
detail::validate_ascii_string<N>(txt);
}
constexpr explicit(false) basic_symbol_text(const basic_fixed_string<char, N>& txt) noexcept :
unicode_(txt), ascii_(txt)
{
detail::validate_ascii_string<N>(txt.data_);
}
constexpr basic_symbol_text(const UnicodeCharT (&u)[N + 1], const char (&a)[M + 1]) noexcept : unicode_(u), ascii_(a)
{
detail::validate_ascii_string<M>(a);
}
constexpr basic_symbol_text(const basic_fixed_string<UnicodeCharT, N>& u,
const basic_fixed_string<char, M>& a) noexcept :
unicode_(u), ascii_(a)
{
detail::validate_ascii_string<M>(a.data_);
}
[[nodiscard]] constexpr auto& unicode() { return unicode_; }
[[nodiscard]] constexpr const auto& unicode() const { return unicode_; }
[[nodiscard]] constexpr auto& ascii() { return ascii_; }
[[nodiscard]] constexpr const auto& ascii() const { return ascii_; }
[[nodiscard]] constexpr bool empty() const { return unicode().empty() && ascii().empty(); }
template<std::size_t N2, std::size_t M2>
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + N2, M + M2> operator+(
const basic_symbol_text& lhs, const basic_symbol_text<UnicodeCharT, N2, M2>& rhs) noexcept
{
return basic_symbol_text<UnicodeCharT, N + N2, M + M2>(lhs.unicode() + rhs.unicode(), lhs.ascii() + rhs.ascii());
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + N2, M + N2> operator+(
const basic_symbol_text& lhs, const basic_fixed_string<UnicodeCharT, N2>& rhs) noexcept
{
return lhs + basic_symbol_text<UnicodeCharT, N2, N2>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + N2, M + N2> operator+(
const basic_fixed_string<UnicodeCharT, N2>& lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<UnicodeCharT, N2, N2>(lhs) + rhs;
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + N2 - 1, M + N2 - 1> operator+(
const basic_symbol_text& lhs, const UnicodeCharT (&rhs)[N2]) noexcept
{
return lhs + basic_symbol_text<UnicodeCharT, N2 - 1, N2 - 1>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + N2 - 1, M + N2 - 1> operator+(
const UnicodeCharT (&lhs)[N2], const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<UnicodeCharT, N2 - 1, N2 - 1>(lhs) + rhs;
}
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + 1, M + 1> operator+(const basic_symbol_text& lhs,
UnicodeCharT rhs) noexcept
{
return lhs + basic_symbol_text<UnicodeCharT, 1, 1>(rhs);
}
[[nodiscard]] constexpr friend basic_symbol_text<UnicodeCharT, N + 1, M + 1> operator+(
UnicodeCharT lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<UnicodeCharT, 1, 1>(lhs) + rhs;
}
template<typename UnicodeCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr auto operator<=>(const basic_symbol_text& lhs,
const basic_symbol_text<UnicodeCharT2, N2, M2>& rhs) noexcept
{
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
if (const auto cmp = lhs.unicode() <=> rhs.unicode(); cmp != 0) return cmp;
UNITS_DIAGNOSTIC_POP
return lhs.ascii() <=> rhs.ascii();
}
template<typename UnicodeCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr bool operator==(const basic_symbol_text& lhs,
const basic_symbol_text<UnicodeCharT2, N2, M2>& rhs) noexcept
{
return lhs.unicode() == rhs.unicode() && lhs.ascii() == rhs.ascii();
}
};
basic_symbol_text(char) -> basic_symbol_text<char, 1, 1>;
template<typename UnicodeCharT>
basic_symbol_text(UnicodeCharT, char) -> basic_symbol_text<UnicodeCharT, 1, 1>;
template<std::size_t N>
basic_symbol_text(const char (&)[N]) -> basic_symbol_text<char, N - 1, N - 1>;
template<std::size_t N>
basic_symbol_text(const basic_fixed_string<char, N>&) -> basic_symbol_text<char, N, N>;
template<typename UnicodeCharT, std::size_t N, std::size_t M>
basic_symbol_text(const UnicodeCharT (&)[N], const char (&)[M]) -> basic_symbol_text<UnicodeCharT, N - 1, M - 1>;
template<typename UnicodeCharT, std::size_t N, std::size_t M>
basic_symbol_text(const basic_fixed_string<UnicodeCharT, N>&, const basic_fixed_string<char, M>&)
-> basic_symbol_text<UnicodeCharT, N, M>;
} // namespace mp_units
@@ -22,10 +22,10 @@
#pragma once
#include <units/bits/external/fixed_string.h>
#include <units/symbol_text.h>
#include <mp-units/bits/external/fixed_string.h>
#include <mp-units/bits/symbol_text.h>
namespace units::detail {
namespace mp_units::detail {
template<std::intmax_t Value>
requires(0 <= Value) && (Value < 10)
@@ -57,7 +57,7 @@ inline constexpr basic_symbol_text superscript_minus("\u207b", "-");
inline constexpr basic_symbol_text superscript_prefix("", "^");
template<std::intmax_t Value>
constexpr auto superscript_helper()
[[nodiscard]] consteval auto superscript_helper()
{
if constexpr (Value < 0)
return superscript_minus + superscript_helper<-Value>();
@@ -68,13 +68,13 @@ constexpr auto superscript_helper()
}
template<std::intmax_t Value>
constexpr auto superscript()
[[nodiscard]] consteval auto superscript()
{
return superscript_prefix + superscript_helper<Value>();
}
template<std::intmax_t Value>
constexpr auto regular()
[[nodiscard]] consteval auto regular()
{
if constexpr (Value < 0)
return basic_fixed_string("-") + superscript_helper<-Value>();
@@ -84,4 +84,4 @@ constexpr auto regular()
return regular<Value / 10>() + regular<Value % 10>();
}
} // namespace units::detail
} // namespace mp_units::detail
@@ -0,0 +1,194 @@
// 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/expression_template.h>
#include <mp-units/bits/magnitude.h>
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/symbol_text.h>
namespace mp_units {
namespace detail {
template<typename T>
inline constexpr bool is_unit = false;
} // namespace detail
/**
* @brief A concept matching all unit types in the library
*
* Satisfied by all unit types provided by the library.
*/
template<typename T>
concept Unit = detail::is_unit<T>;
template<Magnitude auto M, Unit U>
struct scaled_unit;
template<basic_symbol_text Symbol, auto...>
struct named_unit;
namespace detail {
template<basic_symbol_text Symbol, auto... Args>
void to_base_specialization_of_named_unit(const volatile named_unit<Symbol, Args...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_named_unit =
requires(T* t) { to_base_specialization_of_named_unit(t); };
template<typename T>
inline constexpr bool is_specialization_of_named_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_named_unit<named_unit<Symbol, Args...>> = true;
/**
* @brief A concept matching all units with special names
*
* Satisfied by all unit types derived from the specialization of `named_unit`.
*/
template<typename T>
concept NamedUnit =
Unit<T> && detail::is_derived_from_specialization_of_named_unit<T> && (!detail::is_specialization_of_named_unit<T>);
} // namespace detail
/**
* @brief Prevents assignment of a prefix to specific units
*
* By default all named units allow assigning a prefix for them. There are some notable exceptions like
* `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be
* provided.
*/
template<Unit auto V>
inline constexpr bool unit_can_be_prefixed = detail::NamedUnit<std::remove_const_t<decltype(V)>>;
/**
* @brief A concept to be used to define prefixes for a unit
*/
template<typename T>
concept PrefixableUnit = detail::NamedUnit<T> && unit_can_be_prefixed<T{}>;
namespace detail {
template<typename T>
inline constexpr bool is_power_of_unit =
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
template<typename T>
inline constexpr bool is_per_of_units = false;
template<typename... Ts>
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
template<typename T>
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
} // namespace detail
template<detail::DerivedUnitExpr... Expr>
struct derived_unit;
template<basic_symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
requires(!Symbol.empty())
struct prefixed_unit;
namespace detail {
template<auto M, typename U>
void is_unit_impl(const scaled_unit<M, U>*);
template<basic_symbol_text Symbol, auto... Args>
void is_unit_impl(const named_unit<Symbol, Args...>*);
template<typename... Expr>
void is_unit_impl(const derived_unit<Expr...>*);
template<typename T>
inline constexpr bool is_specialization_of_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_unit<named_unit<Symbol, Args...>> = true;
template<typename T>
inline constexpr bool is_specialization_of_prefixed_unit = false;
template<basic_symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
inline constexpr bool is_specialization_of_prefixed_unit<prefixed_unit<Symbol, M, U>> = true;
template<typename T>
requires requires(T* t) { is_unit_impl(t); }
inline constexpr bool is_unit<T> = !is_specialization_of_named_unit<T> && !is_specialization_of_prefixed_unit<T>;
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U);
template<typename U, auto... Vs>
[[nodiscard]] consteval bool has_associated_quantity(power<U, Vs...>)
{
return has_associated_quantity(U{});
}
template<typename... Us>
[[nodiscard]] consteval bool has_associated_quantity(type_list<Us...>)
{
return (... && has_associated_quantity(Us{}));
}
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U)
{
if constexpr (requires { U::quantity_spec; }) return true;
if constexpr (requires { U::reference_unit; })
return has_associated_quantity(U::reference_unit);
else if constexpr (requires { typename U::_num_; })
return has_associated_quantity(typename U::_num_{}) && has_associated_quantity(typename U::_den_{});
else
return false;
}
} // namespace detail
/**
* @brief A concept matching all units that can be used as quantity references
*/
template<typename U>
concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
/**
* @brief A concept matching all units associated with the provided quantity spec
*
* Satisfied by all units associated with the quantity_spec being the instantiation derived from
* the provided quantity_spec type.
*/
template<typename U, auto QS>
concept UnitOf = AssociatedUnit<U> && QuantitySpec<std::remove_const_t<decltype(QS)>> &&
implicitly_convertible(get_quantity_spec(U{}), QS) &&
// the below is to make `dimensionless[radian]` invalid
(get_kind(QS) == get_kind(get_quantity_spec(U{})) ||
!detail::DerivedFromQuantityKindSpecOf<get_quantity_spec(U{}), QS>);
} // namespace mp_units
@@ -0,0 +1,77 @@
// 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/quantity_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/bits/sudo_cast.h>
#include <mp-units/bits/unit_concepts.h>
#include <mp-units/reference.h>
namespace mp_units {
/**
* @brief Explicit cast of a quantity's unit
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
*
* auto d = value_cast<si::second>(1234 * ms);
*
* @tparam ToU a unit to use for a target quantity
*/
template<Unit auto ToU, typename Q>
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
requires Quantity<std::remove_cvref_t<Q>> && (convertible(q.reference, ToU))
{
using q_type = std::remove_reference_t<Q>;
constexpr auto r = [] {
if constexpr (detail::is_specialization_of_reference<std::remove_const_t<decltype(q_type::reference)>> ||
!AssociatedUnit<std::remove_const_t<decltype(ToU)>>)
return reference<q_type::quantity_spec, ToU>{};
else
return ToU;
}();
return detail::sudo_cast<quantity<r, typename q_type::rep>>(std::forward<Q>(q));
}
/**
* @brief Explicit cast of a quantity's representation type
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
*
* auto q = value_cast<int>(1.23 * ms);
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<Representation ToRep, typename Q>
requires Quantity<std::remove_cvref_t<Q>> &&
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
[[nodiscard]] constexpr quantity<std::remove_reference_t<Q>::reference, ToRep> value_cast(Q&& q)
{
return detail::sudo_cast<quantity<q.reference, ToRep>>(std::forward<Q>(q));
}
} // namespace mp_units
@@ -22,7 +22,10 @@
#pragma once
// IWYU pragma: begin_exports
#include <units/bits/basic_concepts.h>
#include <units/bits/quantity_of.h>
// IWYU pragma: end_exports
#include <mp-units/bits/dimension_concepts.h>
#include <mp-units/bits/quantity_concepts.h>
#include <mp-units/bits/quantity_point_concepts.h>
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/bits/unit_concepts.h>
@@ -22,14 +22,12 @@
#pragma once
#include <units/concepts.h>
namespace units::isq {
template<Unit U>
struct dim_electric_current : base_dimension<"I", U> {};
template<typename T>
concept ElectricCurrent = QuantityOfT<T, dim_electric_current>;
} // namespace units::isq
#include <mp-units/concepts.h>
#include <mp-units/customization_points.h>
#include <mp-units/dimension.h>
#include <mp-units/quantity.h>
#include <mp-units/quantity_point.h>
#include <mp-units/quantity_spec.h>
#include <mp-units/reference.h>
#include <mp-units/system_reference.h>
#include <mp-units/unit.h>
@@ -22,11 +22,12 @@
#pragma once
#include <units/bits/external/hacks.h>
#include <mp-units/bits/external/hacks.h>
#include <mp-units/bits/external/type_traits.h>
#include <limits>
#include <type_traits>
namespace units {
namespace mp_units {
/**
* @brief Specifies if a value of a type should be treated as a floating-point value
@@ -40,9 +41,50 @@ namespace units {
template<typename Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool treat_as_floating_point<T> = treat_as_floating_point<typename T::value_type>;
template<typename Rep>
requires requires { typename Rep::value_type; }
inline constexpr bool treat_as_floating_point<Rep> = treat_as_floating_point<typename Rep::value_type>;
template<typename Rep>
requires requires { typename Rep::element_type; }
inline constexpr bool treat_as_floating_point<Rep> = treat_as_floating_point<typename Rep::element_type>;
/**
* @brief Specifies a type to have a scalar character
*
* A scalar is a physical quantity that has magnitude but no direction.
*/
template<typename Rep>
inline constexpr bool is_scalar = std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
/**
* @brief Specifies a type to have a vector character
*
* Vectors are physical quantities that possess both magnitude and direction
* and whose operations obey the axioms of a vector space.
*
* In specific cases a scalar can represent a vector with the default direction.
* If that is the intent, a user should provide a partial specialization:
*
* @code{.cpp}
* template<class T>
* requires mp_units::is_scalar<T>
* inline constexpr bool mp_units::is_vector<T> = true;
* @endcode
*/
template<typename Rep>
inline constexpr bool is_vector = false;
/**
* @brief Specifies a type to have a tensor character
*
* Tensors can be used to describe more general physical quantities.
*
* A vector is a tensor of the first order and a scalar is a tensor of order zero.
* Similarly to `is_vector` a partial specialization is needed in such cases.
*/
template<typename Rep>
inline constexpr bool is_tensor = false;
template<typename T>
requires requires { typename T::element_type; }
@@ -51,7 +93,7 @@ inline constexpr bool treat_as_floating_point<T> = treat_as_floating_point<typen
/**
* @brief A type trait that defines zero, one, min, and max for a representation type
*
* The zero, one, min, and max member functions in units::quantity forward their work to
* The zero, one, min, and max member functions in mp_units::quantity forward their work to
* these methods. This type can be specialized if the representation Rep requires a specific
* implementation to return these quantity objects.
*
@@ -95,8 +137,8 @@ struct quantity_values {
/**
* @brief Provides support for external quantity-like types
*
* The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep,
* and a static member function @c number(T) that will return the raw value of the quantity.
* The type trait should provide the @c reference object, a type alias @c rep,
* and a static member function @c number(T) that returns the raw value of the quantity.
*
* Usage example can be found in @c units/chrono.h header file.
*
@@ -108,8 +150,9 @@ struct quantity_like_traits;
/**
* @brief Provides support for external quantity point-like types
*
* The type trait should provide the following nested type aliases: @c origin, @c unit, @c rep,
* and a static member function @c relative(T) that will return the quantity-like value of the quantity point.
* The type trait should provide nested @c reference and @c origin objects,
* a type alias @c rep, and a static member function @c relative(T) that will return
* the quantity-like value of the quantity point.
*
* Usage example can be found in @c units/chrono.h header file.
*
@@ -118,4 +161,4 @@ struct quantity_like_traits;
template<typename T>
struct quantity_point_like_traits;
} // namespace units
} // namespace mp_units
+213
View File
@@ -0,0 +1,213 @@
// 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/dimension_concepts.h>
#include <mp-units/bits/expression_template.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/symbol_text.h>
namespace mp_units {
/**
* @brief A dimension of a base quantity
*
* Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity
* in the subset can be expressed in terms of the other quantities within that subset. They are referred to as
* being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
* quantities.
*
* `Symbol` template parameter is an unique identifier of the base dimension. The same identifiers can be multiplied
* and divided which will result with an adjustment of its factor in an exponent of a `derived_dimension`
* (in case of zero the dimension will be simplified and removed from further analysis of current expresion).
*
* User should derive a strong type from this class template rather than use it directly in the source code.
* For example:
*
* @code{.cpp}
* inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
* inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
* inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
* @endcode
*
* @note A common convention in this library is to assign the same name for a type and an object of this type.
* Besides defining them user never works with the dimension types in the source code. All operations
* are done on the objects. Contrarily, the dimension types are the only one visible in the compilation
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
*
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
*/
template<basic_symbol_text Symbol>
struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
};
namespace detail {
template<BaseDimension Lhs, BaseDimension Rhs>
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
} // namespace detail
/**
* @brief A dimension of a derived quantity
*
* Derived dimension is an expression of the dependence of a quantity on the base quantities of a system of quantities
* as a product of powers of factors corresponding to the base quantities, omitting any numerical factors.
*
* Instead of using a raw list of exponents this library decided to use expression template syntax to make types
* more digestable for the user. The positive exponents are ordered first and all negative exponents are put as a list
* into the `per<...>` class template. If a power of exponent is different than `1` the dimension type is enclosed in
* `power<Dim, Num, Den>` class template. Otherwise, it is just put directly in the list without any wrapper. There
* is also one special case. In case all of the exponents are negative than the `dimension_one` being a dimension of
* a dimensionless quantity is put in the front to increase the readability.
*
* For example:
*
* @code{.cpp}
* using frequency = decltype(1 / dim_time);
* using speed = decltype(dim_length / dim_time);
* using acceleration = decltype(dim_speed / dim_time);
* using force = decltype(dim_mass * dim_acceleration);
* using energy = decltype(dim_force * dim_length);
* using moment_of_force = decltype(dim_length * dim_force);
* using torque = decltype(dim_moment_of_force);
* @endcode
*
* - `frequency` will be derived from type `derived_dimension<dimension_one, per<dim_time>>`
* - `speed` will be derived from type `derived_dimension<dim_length, per<dim_time>>`
* - `acceleration` will be derived from type `derived_dimension<dim_length, per<power<dim_time, 2>>>`
* - `force` will be derived from type `derived_dimension<dim_length, dim_mass, per<power<dim_time, 2>>>`
* - `energy` will be derived from type `derived_dimension<power<dim_length, 2>, dim_mass, per<power<dim_time, 2>>>`
*
* @note A common convention in this library is to assign the same name for a type and an object of this type.
* Besides defining them user never works with the dimension types in the source code. All operations
* are done on the objects. Contrarily, the dimension types are the only one visible in the compilation
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
*
* @tparam Ds a parameter pack consisting tokens allowed in the dimension specification
* (base dimensions, `dimension_one`, `power<Dim, Num, Den>`, `per<...>`)
*
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the dimensional arithmetic equation provided by the user.
*/
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension : detail::expr_fractions<detail::is_dimension_one, Expr...> {};
/**
* @brief Dimension one
*
* Dimension for which all the exponents of the factors corresponding to the base
* dimensions are zero. It is a dimension of a quantity of dimension one also known as
* "dimensionless".
*/
inline constexpr struct dimension_one : derived_dimension<> {
} dimension_one;
namespace detail {
template<>
struct is_dimension_one<struct dimension_one> : std::true_type {};
} // namespace detail
// Operators
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
{
return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
Lhs{}, Rhs{});
}
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
{
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(Lhs{},
Rhs{});
}
template<Dimension D>
[[nodiscard]] consteval Dimension auto operator/(int value, D)
{
gsl_Expects(value == 1);
return detail::expr_invert<derived_dimension, struct dimension_one>(D{});
}
template<Dimension D>
[[nodiscard]] consteval Dimension auto operator/(D, int) = delete;
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs)
{
return is_same_v<Lhs, Rhs>;
}
/**
* @brief Computes the value of a dimension raised to the `Num/Den` power
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @param d Dimension being the base of the operation
*
* @return Dimension The result of computation
*/
template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
requires detail::non_zero<Den>
[[nodiscard]] consteval Dimension auto pow(D d)
{
if constexpr (detail::BaseDimension<D>) {
if constexpr (Den == 1)
return derived_dimension<power<D, Num>>{};
else
return derived_dimension<power<D, Num, Den>>{};
} else
return detail::expr_pow<Num, Den, derived_dimension, struct dimension_one,
detail::type_list_of_base_dimension_less>(d);
}
/**
* @brief Computes the square root of a dimension
*
* @param d Dimension being the base of the operation
*
* @return Dimension The result of computation
*/
[[nodiscard]] consteval Dimension auto sqrt(Dimension auto d) { return pow<1, 2>(d); }
/**
* @brief Computes the cubic root of a dimension
*
* @param d Dimension being the base of the operation
*
* @return Dimension The result of computation
*/
[[nodiscard]] consteval Dimension auto cbrt(Dimension auto d) { return pow<1, 3>(d); }
// TODO consider adding the support for text output of the dimensional equation
} // namespace mp_units
+534
View File
@@ -0,0 +1,534 @@
// 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/dimension_concepts.h>
#include <mp-units/bits/quantity_concepts.h>
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/bits/sudo_cast.h>
#include <mp-units/bits/unit_concepts.h>
#include <mp-units/customization_points.h>
#include <mp-units/reference.h>
#include <compare>
#include <utility>
// the below is not used in this header but should be exposed with it
#include <mp-units/bits/quantity_cast.h>
#include <mp-units/bits/value_cast.h>
namespace mp_units {
namespace detail {
template<QuantityLike Q>
using quantity_like_type = quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
template<typename T, typename Arg, auto U>
concept RepSafeConstructibleFrom = Unit<std::remove_const_t<decltype(U)>> && std::constructible_from<T, Arg> &&
(treat_as_floating_point<T> || (!treat_as_floating_point<std::remove_cvref_t<Arg>> &&
is_rational(get_canonical_unit(U).mag)));
template<auto UFrom, auto UTo>
concept IntegralConversionFactor = Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag);
template<typename QFrom, typename QTo>
concept QuantityConvertibleTo =
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
convertible(QFrom::unit, QTo::unit) &&
(treat_as_floating_point<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && IntegralConversionFactor<QFrom::unit, QTo::unit>)) &&
// TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
requires(QFrom q) { detail::sudo_cast<QTo>(q); };
template<quantity_character Ch, typename Func, typename T, typename U>
concept InvokeResultOf = std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
template<typename Func, typename Q1, typename Q2>
concept InvocableQuantities = Quantity<Q1> && Quantity<Q2> &&
InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, Func,
typename Q1::rep, typename Q2::rep> &&
requires { common_reference(Q1::reference, Q2::reference); };
template<typename Func, Quantity Q1, Quantity Q2>
requires detail::InvocableQuantities<Func, Q1, Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
} // namespace detail
/**
* @brief A quantity
*
* Property of a phenomenon, body, or substance, where the property has a magnitude that can be
* expressed by means of a number and a reference.
*
* @tparam R a reference of the quantity providing all information about quantity properties
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity {
public:
Rep number_; // needs to be public for a structural type
// member types and values
static constexpr Reference auto reference = R;
static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference);
static constexpr Dimension auto dimension = quantity_spec.dimension;
static constexpr Unit auto unit = get_unit(reference);
using rep = Rep;
// helper used to constrain `make_quantity()` and `operator*(Representation, Unit)`
static constexpr bool _rep_safe_constructible_ = detail::RepSafeConstructibleFrom<Rep, Rep&&, unit>;
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return quantity(quantity_values<rep>::zero());
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return quantity(quantity_values<rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return quantity(quantity_values<rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return quantity(quantity_values<rep>::max());
}
// construction, assignment, destruction
quantity() = default;
quantity(const quantity&) = default;
quantity(quantity&&) = default;
template<detail::QuantityConvertibleTo<quantity> Q>
constexpr explicit(!std::convertible_to<typename Q::rep, Rep>) quantity(const Q& q) :
number_(detail::sudo_cast<quantity>(q).number())
{
}
template<QuantityLike Q>
requires detail::QuantityConvertibleTo<detail::quantity_like_type<Q>, quantity>
constexpr explicit quantity(const Q& q) : quantity(detail::quantity_like_type<Q>(quantity_like_traits<Q>::number(q)))
{
}
template<typename Value>
requires(dimension == dimension_one) &&
(unit == ::mp_units::one) && detail::RepSafeConstructibleFrom<rep, Value&&, unit>
constexpr explicit(!std::convertible_to<Value, Rep>) quantity(Value&& v) : number_(std::forward<Value>(v))
{
}
quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;
// data access
[[nodiscard]] constexpr const rep& number() const& noexcept { return number_; }
[[nodiscard]] constexpr rep&& number() && noexcept { return std::move(number_); }
[[nodiscard]] constexpr const rep&& number() const&& noexcept { return std::move(number_); }
template<Unit U>
requires requires(quantity q) { q[U{}]; }
[[nodiscard]] constexpr rep number_in(U) const noexcept
{
return (*this)[U{}].number();
}
template<Unit U>
requires detail::QuantityConvertibleTo<quantity, quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep>>
[[nodiscard]] constexpr quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep> operator[](U) const
{
return quantity<quantity_spec[U{}], Rep>{*this};
}
// member unary operators
[[nodiscard]] constexpr Quantity auto operator+() const
requires requires(rep v) {
{
+v
} -> std::common_with<rep>;
}
{
return make_quantity<reference>(+number());
}
[[nodiscard]] constexpr Quantity auto operator-() const
requires requires(rep v) {
{
-v
} -> std::common_with<rep>;
}
{
return make_quantity<reference>(-number());
}
constexpr quantity& operator++()
requires requires(rep v) {
{
++v
} -> std::same_as<rep&>;
}
{
++number_;
return *this;
}
[[nodiscard]] constexpr Quantity auto operator++(int)
requires requires(rep v) {
{
v++
} -> std::common_with<rep>;
}
{
return make_quantity<reference>(number_++);
}
constexpr quantity& operator--()
requires requires(rep v) {
{
--v
} -> std::same_as<rep&>;
}
{
--number_;
return *this;
}
[[nodiscard]] constexpr Quantity auto operator--(int)
requires requires(rep v) {
{
v--
} -> std::common_with<rep>;
}
{
return make_quantity<reference>(number_--);
}
constexpr quantity& operator+=(const quantity& q)
requires requires(rep a, rep b) {
{
a += b
} -> std::same_as<rep&>;
}
{
number_ += q.number();
return *this;
}
constexpr quantity& operator-=(const quantity& q)
requires requires(rep a, rep b) {
{
a -= b
} -> std::same_as<rep&>;
}
{
number_ -= q.number();
return *this;
}
template<typename Value>
requires(!Quantity<Value>) && requires(rep a, const Value b) {
{
a *= b
} -> std::same_as<rep&>;
}
constexpr quantity& operator*=(const Value& v)
{
number_ *= v;
return *this;
}
template<QuantityOf<dimension_one> Q>
requires(Q::unit == ::mp_units::one) && requires(rep a, const typename Q::rep b) {
{
a *= b
} -> std::same_as<rep&>;
}
constexpr quantity& operator*=(const Q& rhs)
{
number_ *= rhs.number();
return *this;
}
template<typename Value>
requires(!Quantity<Value>) && requires(rep a, const Value b) {
{
a /= b
} -> std::same_as<rep&>;
}
constexpr quantity& operator/=(const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
number_ /= v;
return *this;
}
template<QuantityOf<dimension_one> Q>
requires(Q::unit == ::mp_units::one) && requires(rep a, const typename Q::rep b) {
{
a /= b
} -> std::same_as<rep&>;
}
constexpr quantity& operator/=(const Q& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
number_ /= rhs.number();
return *this;
}
constexpr quantity& operator%=(const quantity& q)
requires(!treat_as_floating_point<rep>) && requires(rep a, rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(q.number() != quantity_values<rep>::zero());
number_ %= q.number();
return *this;
}
private:
template<Reference auto R2, typename Rep2>
requires quantity<R2, std::remove_cvref_t<Rep2>>::_rep_safe_constructible_
friend constexpr quantity<R2, std::remove_cvref_t<Rep2>> make_quantity(Rep2&& v);
template<typename Value>
requires detail::RepSafeConstructibleFrom<rep, Value&&, unit>
constexpr explicit quantity(Value&& v) : number_(std::forward<Value>(v))
{
}
};
// CTAD
template<QuantityLike Q>
explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
// binary operators on quantities
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator+(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ret = detail::common_quantity_for<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
return make_quantity<ret::reference>(ret(lhs).number() + ret(rhs).number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator-(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ret = detail::common_quantity_for<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
return make_quantity<ret::reference>(ret(lhs).number() - ret(rhs).number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) * get_quantity_spec(R2)).character, std::multiplies<>, Rep1,
Rep2>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
return make_quantity<R1 * R2>(lhs.number() * rhs.number());
}
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep, const Value&>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R, Rep>& q, const Value& v)
{
return make_quantity<R>(q.number() * v);
}
template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, const Value&, Rep>
[[nodiscard]] constexpr Quantity auto operator*(const Value& v, const quantity<R, Rep>& q)
{
return make_quantity<R>(v * q.number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) / get_quantity_spec(R2)).character, std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep2>::zero());
return make_quantity<R1 / R2>(lhs.number() / rhs.number());
}
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::divides<>, Rep, const Value&>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R, Rep>& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
return make_quantity<R>(q.number() / v);
}
template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::divides<>, const Value&, Rep>
[[nodiscard]] constexpr Quantity auto operator/(const Value& v, const quantity<R, Rep>& q)
{
return make_quantity<::mp_units::one / R>(v / q.number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires(!treat_as_floating_point<Rep1>) && (!treat_as_floating_point<Rep2>) &&
detail::InvocableQuantities<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep1>::zero());
using ret = detail::common_quantity_for<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
return make_quantity<ret::reference>(ret(lhs).number() % ret(rhs).number());
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires requires { typename std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>; } &&
std::equality_comparable<typename std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>::rep>
[[nodiscard]] constexpr bool operator==(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ct = std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>;
return ct(lhs).number() == ct(rhs).number();
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires requires { typename std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>; } &&
std::three_way_comparable<typename std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>::rep>
[[nodiscard]] constexpr auto operator<=>(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ct = std::common_type_t<quantity<R1, Rep1>, quantity<R2, Rep2>>;
return ct(lhs).number() <=> ct(rhs).number();
}
// binary operators on dimensionless quantities
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) && (quantity<R, Rep>::dimension == dimension_one) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::plus<>, Rep, Value>
[[nodiscard]] constexpr Quantity auto operator+(const quantity<R, Rep>& q, const Value& v)
{
return q + make_quantity<::mp_units::one>(v);
}
template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) && (quantity<R, Rep>::dimension == dimension_one) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::plus<>, Value, Rep>
[[nodiscard]] constexpr Quantity auto operator+(const Value& v, const quantity<R, Rep>& q)
{
return make_quantity<::mp_units::one>(v) + q;
}
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) && (quantity<R, Rep>::dimension == dimension_one) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::minus<>, Rep, Value>
[[nodiscard]] constexpr Quantity auto operator-(const quantity<R, Rep>& q, const Value& v)
{
return q - make_quantity<::mp_units::one>(v);
}
template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) && (quantity<R, Rep>::dimension == dimension_one) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::minus<>, Value, Rep>
[[nodiscard]] constexpr Quantity auto operator-(const Value& v, const quantity<R, Rep>& q)
{
return make_quantity<::mp_units::one>(v) - q;
}
template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) && (quantity<R, Rep>::dimension == dimension_one) && (get_unit(R) == ::mp_units::one) &&
(!treat_as_floating_point<Value>) && (!treat_as_floating_point<Rep>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::modulus<>, Value, Rep>
[[nodiscard]] constexpr Quantity auto operator%(const Value& v, const quantity<R, Rep>& q)
{
gsl_ExpectsAudit(q.number() != quantity_values<Rep>::zero());
return make_quantity<R>(v % q.number());
}
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) &&
(quantity<R, Rep>::dimension == dimension_one) && std::equality_comparable_with<Rep, Value>
[[nodiscard]] constexpr bool operator==(const quantity<R, Rep>& q, const Value& v)
{
return q == make_quantity<::mp_units::one>(v);
}
template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) &&
(quantity<R, Rep>::dimension == dimension_one) && std::three_way_comparable_with<Rep, Value>
[[nodiscard]] constexpr auto operator<=>(const quantity<R, Rep>& q, const Value& v)
{
return q <=> make_quantity<::mp_units::one>(v);
}
// make_quantity
template<Reference auto R, typename Rep>
requires quantity<R, std::remove_cvref_t<Rep>>::_rep_safe_constructible_
[[nodiscard]] constexpr quantity<R, std::remove_cvref_t<Rep>> make_quantity(Rep&& v)
{
return quantity<R, std::remove_cvref_t<Rep>>(std::forward<Rep>(v));
}
} // namespace mp_units
namespace std {
template<mp_units::Quantity Q1, mp_units::Quantity Q2>
requires requires {
{
mp_units::common_reference(Q1::reference, Q2::reference)
} -> mp_units::Reference;
typename common_type_t<typename Q1::rep, typename Q2::rep>;
}
struct common_type<Q1, Q2> {
public:
using type = mp_units::quantity<mp_units::common_reference(Q1::reference, Q2::reference),
common_type_t<typename Q1::rep, typename Q2::rep>>;
};
// dimensionless quantities support for interacting with raw values
template<mp_units::Quantity Q, typename Value>
requires(!mp_units::Quantity<Value>) && (Q::dimension == mp_units::dimension_one) && (Q::unit == mp_units::one) &&
requires { typename common_type_t<typename Q::rep, Value>; }
struct common_type<Q, Value> {
public:
using type = mp_units::quantity<mp_units::one, common_type_t<typename Q::rep, Value>>;
};
template<mp_units::Quantity Q, typename Value>
requires(!mp_units::Quantity<Value>) && (Q::dimension == mp_units::dimension_one) && (Q::unit == mp_units::one) &&
requires { typename common_type_t<typename Q::rep, Value>; }
struct common_type<Value, Q> : common_type<Q, Value> {};
} // namespace std
+257
View File
@@ -0,0 +1,257 @@
// 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/quantity_point_concepts.h>
#include <mp-units/customization_points.h>
#include <mp-units/quantity.h>
#include <compare>
namespace mp_units {
template<QuantitySpec auto Q>
struct absolute_point_origin {
static constexpr QuantitySpec auto quantity_spec = Q;
absolute_point_origin() = default;
template<QuantitySpec auto Q2>
requires(implicitly_convertible(Q2, Q))
consteval absolute_point_origin(absolute_point_origin<Q2>)
{
}
};
namespace detail {
[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po)
{
if constexpr (requires { po.absolute_point_origin; })
return po.absolute_point_origin;
else
return po;
}
template<QuantityPointLike QP>
using quantity_point_like_type =
quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;
} // namespace detail
/**
* @brief A quantity point
*
* An absolute quantity measured from an origin.
*
* @tparam R a reference of the quantity point providing all information about quantity properties
* @tparam PO a type that represents the origin point from which the quantity point is measured from
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = absolute_point_origin<get_quantity_spec(R)>{},
RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity_point {
public:
// member types and values
static constexpr Reference auto reference = R;
static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference);
static constexpr Dimension auto dimension = quantity_spec.dimension;
static constexpr Unit auto unit = get_unit(reference);
static constexpr PointOrigin auto absolute_point_origin = detail::get_absolute_point_origin(PO);
static constexpr PointOrigin auto point_origin = PO;
using rep = Rep;
using quantity_type = quantity<reference, Rep>;
quantity_type q_; // needs to be public for a structural type
// static member functions
[[nodiscard]] static constexpr quantity_point min() noexcept
requires requires { quantity_type::min(); }
{
return quantity_point{quantity_type::min()};
}
[[nodiscard]] static constexpr quantity_point max() noexcept
requires requires { quantity_type::max(); }
{
return quantity_point{quantity_type::max()};
}
// construction, assignment, destruction
quantity_point() = default;
quantity_point(const quantity_point&) = default;
quantity_point(quantity_point&&) = default;
template<typename T>
requires std::constructible_from<quantity_type, T>
constexpr explicit quantity_point(T&& v) : q_(std::forward<T>(v))
{
}
template<QuantityPointOf<point_origin> QP>
requires std::constructible_from<quantity_type, typename QP::quantity_type>
constexpr explicit(!std::convertible_to<typename QP::quantity_type, quantity_type>) quantity_point(const QP& qp) :
q_(qp.relative())
{
}
template<QuantityPointLike QP>
requires std::same_as<std::remove_const_t<decltype(quantity_point_like_traits<QP>::point_origin)>,
std::remove_const_t<decltype(point_origin)>> &&
std::convertible_to<typename detail::quantity_point_like_type<QP>::quantity_type, quantity_type>
constexpr explicit quantity_point(const QP& qp) :
q_(typename detail::quantity_point_like_type<QP>::quantity_type{quantity_point_like_traits<QP>::relative(qp)})
{
}
quantity_point& operator=(const quantity_point&) = default;
quantity_point& operator=(quantity_point&&) = default;
// data access
[[nodiscard]] constexpr const quantity_type& relative() const& noexcept { return q_; }
[[nodiscard]] constexpr quantity_type&& relative() && noexcept { return std::move(q_); }
[[nodiscard]] constexpr const quantity_type&& relative() const&& noexcept { return std::move(q_); }
[[nodiscard]] constexpr Quantity auto absolute() const noexcept
{
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<
std::remove_const_t<decltype(point_origin)>>)
return relative();
else
return point_origin.absolute() + relative();
}
template<Unit U>
requires detail::QuantityConvertibleTo<quantity_type, quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep>>
[[nodiscard]] constexpr quantity_point<::mp_units::reference<quantity_spec, U{}>{}, PO, Rep> operator[](U) const
{
return quantity_point<quantity_spec[U{}], PO, Rep>{*this};
}
// member unary operators
constexpr quantity_point& operator++()
requires requires { ++q_; }
{
++q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator++(int)
requires requires { q_++; }
{
return quantity_point(q_++);
}
constexpr quantity_point& operator--()
requires requires { --q_; }
{
--q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator--(int)
requires requires { q_--; }
{
return quantity_point(q_--);
}
constexpr quantity_point& operator+=(const quantity_type& q)
requires requires { q_ += q; }
{
q_ += q;
return *this;
}
constexpr quantity_point& operator-=(const quantity_type& q)
requires requires { q_ -= q; }
{
q_ -= q;
return *this;
}
};
// CTAD
template<Representation Rep>
explicit quantity_point(Rep) -> quantity_point<dimensionless[one], absolute_point_origin<dimensionless>{}, Rep>;
template<Quantity Q>
explicit quantity_point(Q) -> quantity_point<Q::reference, absolute_point_origin<Q::quantity_spec>{}, typename Q::rep>;
template<QuantityLike Q>
explicit quantity_point(Q)
-> quantity_point<quantity_like_traits<Q>::reference, absolute_point_origin<quantity_like_traits<Q>::quantity_spec>{},
typename quantity_like_traits<Q>::rep>;
template<QuantityPointLike QP>
explicit quantity_point(QP)
-> quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point<R1, PO1, Rep1>& lhs,
const quantity<R2, Rep2>& rhs)
requires requires { lhs.relative() + rhs; }
{
const auto q = lhs.relative() + rhs;
using q_type = decltype(q);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(q);
}
template<auto R1, typename Rep1, auto R2, auto PO2, typename Rep2>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity<R1, Rep1>& lhs,
const quantity_point<R2, PO2, Rep2>& rhs)
requires requires { rhs + lhs; }
{
return rhs + lhs;
}
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point<R1, PO1, Rep1>& lhs,
const quantity<R2, Rep2>& rhs)
requires requires { lhs.relative() - rhs; }
{
const auto q = lhs.relative() - rhs;
using q_type = decltype(q);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(q);
}
template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
[[nodiscard]] constexpr Quantity auto operator-(const QP1& lhs, const QP2& rhs)
requires requires { lhs.absolute() - rhs.absolute(); }
{
return lhs.absolute() - rhs.absolute();
}
template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
requires std::three_way_comparable_with<typename QP1::quantity_type, typename QP2::quantity_type>
[[nodiscard]] constexpr auto operator<=>(const QP1& lhs, const QP2& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
requires std::equality_comparable_with<typename QP1::quantity_type, typename QP2::quantity_type>
[[nodiscard]] constexpr bool operator==(const QP1& lhs, const QP2& rhs)
{
return lhs.relative() == rhs.relative();
}
} // namespace mp_units
File diff suppressed because it is too large Load Diff
+219
View File
@@ -0,0 +1,219 @@
// 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/get_associated_quantity.h>
#include <mp-units/bits/quantity_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
namespace mp_units {
template<Reference auto R, typename Rep>
requires quantity<R, std::remove_cvref_t<Rep>>::_rep_safe_constructible_
[[nodiscard]] constexpr quantity<R, std::remove_cvref_t<Rep>> make_quantity(Rep&& v);
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u)
{
return detail::get_associated_quantity(u);
}
/**
* @brief Quantity reference type
*
* Quantity reference describes all the properties of a quantity besides its
* representation type.
*
* In most cases this class template is not explicitly instantiated by the user.
* It is implicitly instantiated by the library's framework while binding a quantity
* specification with a compatible unit.
*
* @code{.cpp}
* Reference auto kmph = isq::speed[km / h];
* QuantityOf<isq::speed[km / h]> auto speed = 90 * kmph;
* @endcode
*
* The following syntaxes are not allowed:
* `2 / kmph`, `kmph * 3`, `kmph / 4`, `70 * isq::length[km] / isq:time[h]`.
*/
template<QuantitySpec auto Q, Unit auto U>
struct reference {
template<auto Q2, auto U2>
[[nodiscard]] friend consteval bool operator==(reference, reference<Q2, U2>)
{
return Q == Q2 && U == U2;
}
template<AssociatedUnit U2>
[[nodiscard]] friend consteval bool operator==(reference, U2 u2)
{
return Q == get_quantity_spec(u2) && U == u2;
}
template<auto Q2, auto U2>
[[nodiscard]] friend consteval reference<Q * Q2, U * U2> operator*(reference, reference<Q2, U2>)
{
return {};
}
template<AssociatedUnit U2>
[[nodiscard]] friend consteval reference<Q * get_quantity_spec(U2{}), U * U2{}> operator*(reference, U2)
{
return {};
}
template<AssociatedUnit U1>
[[nodiscard]] friend consteval reference<get_quantity_spec(U1{}) * Q, U1{} * U> operator*(U1, reference)
{
return {};
}
template<auto Q2, auto U2>
[[nodiscard]] friend consteval reference<Q / Q2, U / U2> operator/(reference, reference<Q2, U2>)
{
return {};
}
template<AssociatedUnit U2>
[[nodiscard]] friend consteval reference<Q / get_quantity_spec(U2{}), U / U2{}> operator/(reference, U2)
{
return {};
}
template<AssociatedUnit U1>
[[nodiscard]] friend consteval reference<get_quantity_spec(U1{}) / Q, U1{} / U> operator/(U1, reference)
{
return {};
}
/**
* @brief Computes the value of a reference raised to the `Num/Den` power
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @param r Reference being the base of the operation
*
* @return The result of computation
*/
template<std::intmax_t Num, std::intmax_t Den = 1>
requires detail::non_zero<Den>
[[nodiscard]] friend consteval reference<pow<Num, Den>(Q), pow<Num, Den>(U)> pow(reference)
{
return {};
}
/**
* @brief Computes the square root of a reference
*
* @param r Reference being the base of the operation
*
* @return The result of computation
*/
[[nodiscard]] friend consteval reference<sqrt(Q), sqrt(U)> sqrt(reference) { return {}; }
/**
* @brief Computes the cubic root of a reference
*
* @param r Reference being the base of the operation
*
* @return The result of computation
*/
[[nodiscard]] friend consteval reference<cbrt(Q), cbrt(U)> cbrt(reference) { return {}; }
template<auto Q2, auto U2>
[[nodiscard]] friend consteval bool convertible(reference, reference<Q2, U2>)
{
return implicitly_convertible(Q, Q2) && convertible(U, U2);
}
template<AssociatedUnit U2>
[[nodiscard]] friend consteval bool convertible(reference, U2 u2)
{
return implicitly_convertible(Q, get_quantity_spec(u2)) && convertible(U, u2);
}
template<AssociatedUnit U1>
[[nodiscard]] friend consteval bool convertible(U1 u1, reference)
{
return implicitly_convertible(get_quantity_spec(u1), Q) && convertible(u1, U);
}
};
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
template<typename Rep, Reference R>
requires quantity<R{}, std::remove_cvref_t<Rep>>::_rep_safe_constructible_
[[nodiscard]] constexpr quantity<R{}, std::remove_cvref_t<Rep>> operator*(Rep&& lhs, R)
{
return make_quantity<R{}>(std::forward<Rep>(lhs));
}
void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete;
[[nodiscard]] consteval AssociatedUnit auto common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2,
AssociatedUnit auto... rest)
requires requires {
{
common_quantity_spec(get_quantity_spec(u1), get_quantity_spec(u2), get_quantity_spec(rest)...)
} -> QuantitySpec;
{
common_unit(u1, u2, rest...)
} -> AssociatedUnit;
}
{
return common_unit(u1, u2, rest...);
}
template<Reference R1, Reference R2, Reference... Rest>
[[nodiscard]] consteval Reference auto common_reference(R1 r1, R2 r2, Rest... rest)
requires(!(AssociatedUnit<R1> && AssociatedUnit<R2> && (... && AssociatedUnit<Rest>))) && requires {
{
common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...)
} -> QuantitySpec;
{
common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...)
} -> Unit;
}
{
return reference<common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...),
common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...)>{};
}
namespace detail {
template<AssociatedUnit auto To, AssociatedUnit From>
[[nodiscard]] consteval std::remove_const_t<decltype(To)> clone_reference_with(From)
{
return {};
}
template<Unit auto To, QuantitySpec auto QS, Unit auto U>
[[nodiscard]] consteval reference<QS, To> clone_reference_with(reference<QS, U>)
{
return {};
}
} // namespace detail
} // namespace mp_units
@@ -0,0 +1,72 @@
// 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/quantity_spec.h>
#include <mp-units/reference.h>
#include <mp-units/unit.h>
namespace mp_units {
/**
* @brief System-specific reference
*
* This type is used in rare cases where more than one base quantity in a specific
* system of units uses the same unit. For example in a hypothetical system of natural units
* where constant for speed of light `c = 1`, length and time could be measured in seconds.
* In such cases `system_reference` has to be used to explicitly express such a binding.
*
* For example:
*
* @code{.cpp}
* // hypothetical natural system of units for c=1
*
* inline constexpr struct second : named_unit<"s"> {} second;
* inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute;
* inline constexpr struct gram : named_unit<"g"> {} gram;
* inline constexpr struct kilogram : decltype(si::kilo<gram>) {} kilogram;
*
* inline constexpr struct time : system_reference<isq::time, second> {} time;
* inline constexpr struct length : system_reference<isq::length, second> {} length;
* inline constexpr struct speed : system_reference<isq::speed, second / second> {} speed;
* inline constexpr struct force : system_reference<isq::force, kilogram / second> {} force;
* @endcode
*
* @tparam Q quantity for which a unit is being assigned
* @tparam CoU coherent unit for a quantity in this system
*/
template<QuantitySpec auto Q, Unit auto CoU>
requires(!AssociatedUnit<std::remove_const_t<decltype(CoU)>>) || (CoU == one)
struct system_reference {
static constexpr auto quantity_spec = Q;
static constexpr auto coherent_unit = CoU;
template<Unit U>
requires(convertible(coherent_unit, U{}))
[[nodiscard]] constexpr reference<quantity_spec, U{}> operator[](U) const
{
return {};
}
};
} // namespace mp_units
+817
View File
@@ -0,0 +1,817 @@
// 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/algorithm.h>
#include <mp-units/bits/expression_template.h>
#include <mp-units/bits/external/fixed_string.h>
#include <mp-units/bits/external/type_name.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/get_associated_quantity.h>
#include <mp-units/bits/magnitude.h>
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/symbol_text.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/bits/unit_concepts.h>
#include <iterator>
#include <string>
namespace mp_units {
/**
* @brief Unit being a scaled version of another unit
*
* @tparam M magnitude describing the scale factor
* @tparam U reference unit being scaled
*
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the unit arithmetic equation provided by the user.
*/
template<Magnitude auto M, Unit U>
struct scaled_unit {
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M;
static constexpr U reference_unit{};
};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_scaled_unit = false;
template<auto M, Unit U>
inline constexpr bool is_specialization_of_scaled_unit<scaled_unit<M, U>> = true;
} // namespace detail
/**
* @brief A named unit
*
* Defines a unit with a special name. It may be used to provide a base unit in the system
* of units (i.e. `metre`) or a name assigned to another scaled or derived unit
* (i.e. `hour`, `joule`).
* Most of the named units may be composed with a prefix to create a `prefixed_unit`.
*
* For example:
*
* @code{.cpp}
* inline constexpr struct second : named_unit<"s", time> {} second;
* inline constexpr struct metre : named_unit<"m", length> {} metre;
* inline constexpr struct hertz : named_unit<"Hz", 1 / second> {} hertz;
* inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton;
* inline constexpr struct degree_Celsius : named_unit<basic_symbol_text{"°C", "`C"}, kelvin> {} degree_Celsius;
* inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute;
* @endcode
*
* @note A common convention in this library is to assign the same name for a type and an object of this type.
* Besides defining them user never works with the unit types in the source code. All operations
* are done on the objects. Contrarily, the unit types are the only one visible in the compilation
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
*
* @tparam Symbol a short text representation of the unit
*/
template<basic_symbol_text Symbol, auto...>
struct named_unit;
/**
* @brief Specialization for unit of a specified base quantity
*
* Associates a unit with a specified base quantity.
* For example `si::metre` is a unit to measure `isq::length` in the SI system.
*
* @note This is the preferred way to define a measurement unit for a specific base quantity.
*
* @note It does not have to (or sometimes even can't) be a proper system's base unit. For example
* a base unit of mass in the SI is `si::kilogram` but here you are about to provide an `si::gram`
* and it will work just fine as those two are convertible to each other. A similar case would be
* the `cgs::centimetre` that is a base unit for `isq::length` in the CGS system.
*
* @tparam Symbol a short text representation of the unit
* @tparam QuantitySpec a specification of a base quantity to be measured with this unit
*/
template<basic_symbol_text Symbol, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
struct named_unit<Symbol, QS> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto quantity_spec = QS;
};
/**
* @brief Specialization for a unit that can be reused by several base quantities
*
* This specialization is used in rare cases where more than one base quantity in a specific
* system of units uses the same unit. For example in a hypothetical system of natural units
* where constant for speed of light `c = 1`, length and time could be measured in seconds.
* In such cases `system_reference` has to be used to explicitly express such a binding.
*
* @tparam Symbol a short text representation of the unit
*/
template<basic_symbol_text Symbol>
requires(!Symbol.empty())
struct named_unit<Symbol> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
};
/**
* @brief Specialization for a unit with special name
*
* Allows assigning a special name to another scaled or derived unit (i.e. `hour`, `joule`).
*
* @tparam Symbol a short text representation of the unit
* @tparam Unit a unit for which we provide a special name
*/
template<basic_symbol_text Symbol, Unit auto U>
requires(!Symbol.empty())
struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
};
/**
* @brief Specialization for a unit with special name valid only for a specific quantity
*
* The same as the above but additionally limits the usage of this unit to provided quantities.
*
* @tparam Symbol a short text representation of the unit
* @tparam Unit a unit for which we provide a special name
* @tparam QuantitySpec a specification of a quantity to be measured with this unit
*/
template<basic_symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto quantity_spec = QS;
};
/**
* @brief A prefixed unit
*
* Defines a new unit that is a scaled version of another unit with the scaling
* factor specified by a predefined prefix.
*
* For example:
*
* @code{.cpp}
* template<PrefixableUnit auto U>
* struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
*
* template<PrefixableUnit auto U>
* inline constexpr kilo_<U> kilo;
*
* inline constexpr struct kilogram : decltype(si::kilo<gram>) {} kilogram;
* @endcode
*
* @tparam Symbol a prefix text to prepend to a unit symbol
* @tparam M scaling factor of the prefix
* @tparam U a named unit to be prefixed
*/
template<basic_symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
requires(!Symbol.empty())
struct prefixed_unit : std::remove_const_t<decltype(M * U)> {
static constexpr auto symbol = Symbol + U.symbol;
};
namespace detail {
template<typename T>
struct is_one : std::false_type {};
} // namespace detail
/**
* @brief Measurement unit for a derived quantity
*
* Derived units are defined as products of powers of the base units.
*
* Instead of using a raw list of exponents this library decided to use expression template syntax to make types
* more digestable for the user. The positive exponents are ordered first and all negative exponents are put as a list
* into the `per<...>` class template. If a power of exponent is different than `1` the unit type is enclosed in
* `power<Dim, Num, Den>` class template. Otherwise, it is just put directly in the list without any wrapper. There
* is also one special case. In case all of the exponents are negative then the `one` being a coherent unit of
* a dimensionless quantity is put in the front to increase the readability.
*
* For example:
*
* @code{.cpp}
* static_assert(is_of_type<1 / second, derived_unit<one, per<second>>>);
* static_assert(is_of_type<1 / (1 / second), second>);
* static_assert(is_of_type<one * second, second>);
* static_assert(is_of_type<metre * metre, derived_unit<power<metre, 2>>>);
* static_assert(is_of_type<metre * second, derived_unit<metre, second>>);
* static_assert(is_of_type<metre / second, derived_unit<metre, per<second>>>);
* static_assert(is_of_type<metre / square(second), derived_unit<metre, per<power<second, 2>>>>);
* static_assert(is_of_type<watt / joule, derived_unit<watt, per<joule>>>);
* @endcode
*
* Every unit in the library has its internal canonical representation being the list of exponents of named base units
* (with the exception of `kilogram` which is represented as `gram` here) and a scaling ratio represented with a
* magnitude.
*
* Two units are deemed convertible if their canonical version has units of the same type.
* Two units are equivalent when they are convertible and their canonical versions have the same scaling ratios.
*
* The above means that:
* - `1/s` and `Hz` are both convertible and equal
* - `m` and `km` are convertible but not equal
* - `m` and `m²` ane not convertible and not equal
*
* @note This also means that units like `hertz` and `becquerel` are also considered convertible and equal.
*
* @tparam Us a parameter pack consisting tokens allowed in the unit specification
* (units, `power<U, Num, Den>`, `per<...>`)
*
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the unit arithmetic equation provided by the user.
*/
template<detail::DerivedUnitExpr... Expr>
struct derived_unit : detail::expr_fractions<detail::is_one, Expr...> {};
/**
* @brief Unit one
*
* Unit of a dimensionless quantity.
*/
// clang-format off
inline constexpr struct one : derived_unit<> {} one;
// clang-format on
namespace detail {
template<>
struct is_one<struct one> : std::true_type {};
/**
* @brief A canonical representation of a unit
*
* A canonical representation of a unit consists of a `reference_unit` and its scaling
* factor represented by the magnitude `mag`.
*
* `reference_unit` is a unit (possibly derived one) that consists only named base units.
* All of the intermediate derived units are extracted, prefixes and magnitudes of scaled
* units are stripped from them and accounted in the `mag`.
*
* All units having the same canonical unit are deemed equal.
* All units having the same `reference_unit` are convertible (their `mag` may differ
* and is the subject of conversion).
*
* @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit
*/
template<Magnitude M, Unit U>
struct canonical_unit {
M mag;
U reference_unit;
};
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
template<Unit T, basic_symbol_text Symbol>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&);
template<Unit T, basic_symbol_text Symbol, Unit auto U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&);
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Expr...>&);
template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit<M, U>&)
{
auto base = get_canonical_unit_impl(U{}, U{});
return canonical_unit{M * base.mag, base.reference_unit};
}
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
{
return canonical_unit{mag<1>, t};
}
template<Unit T, basic_symbol_text Symbol>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&)
{
return canonical_unit{mag<1>, t};
}
template<Unit T, basic_symbol_text Symbol, Unit auto U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&)
{
return get_canonical_unit_impl(U, U);
}
template<typename F, int Num, int... Den, typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(const power<F, Num, Den...>&, const type_list<Us...>&)
{
auto mag = (mp_units::mag<1> * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).mag));
auto u = (one * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).reference_unit));
return canonical_unit{mag, u};
}
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&)
{
auto base = get_canonical_unit_impl(F{}, F{});
if constexpr (requires { typename decltype(base.reference_unit)::_num_; }) {
auto num = get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base.reference_unit)::_num_{});
auto den = get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base.reference_unit)::_den_{});
return canonical_unit{pow<Num, Den...>(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit};
} else {
return canonical_unit{pow<Num, Den...>(base.mag),
derived_unit<power<std::remove_const_t<decltype(base.reference_unit)>, Num, Den...>>{}};
}
}
template<typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(const type_list<Us...>&)
{
auto mag = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag);
auto u = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit);
return canonical_unit{mag, u};
}
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Expr...>&)
{
auto num = get_canonical_unit_impl(typename derived_unit<Expr...>::_num_{});
auto den = get_canonical_unit_impl(typename derived_unit<Expr...>::_den_{});
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
}
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); }
template<Unit Lhs, Unit Rhs>
[[nodiscard]] consteval bool less(Lhs, Rhs)
{
if constexpr (requires {
Lhs::symbol;
Rhs::symbol;
})
// prefer symbols comparison if possible as it gives typically better results
// i.e. it puts upper case in from so `N m` is correct
return Lhs::symbol < Rhs::symbol;
else
return type_name<Lhs>() < type_name<Rhs>();
}
// TODO What if the same unit will have different types (i.e. user will inherit its own type from `metre`)?
// Is there a better way to sort units here? Some of them may not have symbol at all (like all units of
// dimensionless quantities).
template<Unit Lhs, Unit Rhs>
struct unit_less : std::bool_constant<less(Lhs{}, Rhs{})> {};
template<typename T1, typename T2>
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
} // namespace detail
// Operators
/**
* Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned.
*/
template<Magnitude M, Unit U>
[[nodiscard]] consteval Unit auto operator*(M mag, const U u)
{
if constexpr (mag == mp_units::mag<1>)
return u;
else
return scaled_unit<mag, U>{};
}
[[nodiscard]] consteval Unit auto operator*(Unit auto, Magnitude auto) = delete;
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] consteval Unit auto operator*(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit * rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return Rhs::mag * (lhs * Rhs::reference_unit);
else
return detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
}
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] consteval Unit auto operator/(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit / rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit);
else
return detail::expr_divide<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
}
[[nodiscard]] consteval Unit auto operator/(int value, Unit auto u)
{
gsl_Expects(value == 1);
return detail::expr_invert<derived_unit, struct one>(u);
}
[[nodiscard]] consteval Unit auto operator/(Unit auto, int) = delete;
namespace detail {
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(...) { return false; }
template<basic_symbol_text Symbol, auto... D>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const named_unit<Symbol, D...>&,
const named_unit<Symbol, D...>&)
{
return true;
}
template<typename F1, typename F2, auto... Vs>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const power<F1, Vs...>&, const power<F2, Vs...>&)
{
return have_same_canonical_reference_unit_impl(F1{}, F2{});
}
template<typename... Us1, typename... Us2>
requires(sizeof...(Us1) == sizeof...(Us2))
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const type_list<Us1...>&, const type_list<Us2...>&)
{
return (... && have_same_canonical_reference_unit_impl(Us1{}, Us2{}));
}
template<typename... Expr1, typename... Expr2>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const derived_unit<Expr1...>&,
const derived_unit<Expr2...>&)
{
return have_same_canonical_reference_unit_impl(typename derived_unit<Expr1...>::_num_{},
typename derived_unit<Expr2...>::_num_{}) &&
have_same_canonical_reference_unit_impl(typename derived_unit<Expr1...>::_den_{},
typename derived_unit<Expr2...>::_den_{});
}
[[nodiscard]] consteval bool have_same_canonical_reference_unit(Unit auto u1, Unit auto u2)
{
auto canonical_lhs = detail::get_canonical_unit(u1);
auto canonical_rhs = detail::get_canonical_unit(u2);
return have_same_canonical_reference_unit_impl(canonical_lhs.reference_unit, canonical_rhs.reference_unit);
}
} // namespace detail
[[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
{
auto canonical_lhs = detail::get_canonical_unit(lhs);
auto canonical_rhs = detail::get_canonical_unit(rhs);
return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
canonical_lhs.mag == canonical_rhs.mag;
}
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_derived_unit = false;
template<typename... Expr>
inline constexpr bool is_specialization_of_derived_unit<derived_unit<Expr...>> = true;
} // namespace detail
/**
* @brief Computes the value of a unit raised to the `Num/Den` power
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @param u Unit being the base of the operation
*
* @return Unit The result of computation
*/
template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
requires detail::non_zero<Den>
[[nodiscard]] consteval Unit auto pow(U u)
{
if constexpr (Num == 0 || is_same_v<U, struct one>)
return one;
else if constexpr (ratio{Num, Den} == 1)
return u;
else if constexpr (detail::is_specialization_of_scaled_unit<U>)
return scaled_unit<pow<Num, Den>(U::mag), std::remove_const_t<decltype(pow<Num, Den>(U::reference_unit))>>{};
else if constexpr (detail::is_specialization_of_derived_unit<U>)
return detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u);
else if constexpr (Den == 1)
return derived_unit<power<U, Num>>{};
else
return derived_unit<power<U, Num, Den>>{};
}
/**
* @brief Computes the square root of a unit
*
* @param u Unit being the base of the operation
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return pow<1, 2>(u); }
/**
* @brief Computes the cubic root of a unit
*
* @param u Unit being the base of the operation
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return pow<1, 3>(u); }
/**
* @brief Computes the square power of a unit
*
* @param u Unit being the base of the operation
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto square(Unit auto u) { return pow<2>(u); }
/**
* @brief Computes the cubic power of a unit
*
* @param u Unit being the base of the operation
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return pow<3>(u); }
// common dimensionless units
// clang-format off
inline constexpr struct percent : named_unit<"%", mag<ratio{1, 100}> * one> {} percent;
inline constexpr struct per_mille : named_unit<basic_symbol_text{"‰", "%o"}, mag<ratio(1, 1000)> * one> {} per_mille;
// clang-format on
// convertible_to
[[nodiscard]] consteval bool convertible(Unit auto from, Unit auto to)
{
return detail::have_same_canonical_reference_unit(from, to);
}
// Common unit
[[nodiscard]] consteval Unit auto common_unit(Unit auto u) { return u; }
template<Unit U1, Unit U2>
[[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2)
requires(detail::have_same_canonical_reference_unit(u1, u2))
{
if constexpr (U1{} == U2{}) {
if constexpr (std::derived_from<U1, U2>)
return u1;
else if constexpr (std::derived_from<U2, U1>)
return u2;
else
// TODO Check if there is a better choice here
return better_type_name(u1, u2);
} else {
constexpr auto canonical_lhs = detail::get_canonical_unit(U1{});
constexpr auto canonical_rhs = detail::get_canonical_unit(U2{});
if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag))
return u2;
else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag))
return u1;
else {
constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag);
return scaled_unit<cm, std::remove_const_t<decltype(canonical_lhs.reference_unit)>>{};
}
}
}
[[nodiscard]] consteval Unit auto common_unit(Unit auto u1, Unit auto u2, Unit auto u3, Unit auto... rest)
requires requires { common_unit(common_unit(u1, u2), u3, rest...); }
{
return common_unit(common_unit(u1, u2), u3, rest...);
}
// get_unit_symbol
enum class text_encoding {
unicode, // m³; µs
ascii, // m^3; us
default_encoding = unicode
};
enum class unit_symbol_solidus {
one_denominator, // m/s; kg m-1 s-1
always, // m/s; kg/(m s)
never, // m s-1; kg m-1 s-1
default_denominator = one_denominator
};
enum class unit_symbol_separator {
space, // kg m²/s²
dot, // kg⋅m²/s² (valid only for unicode encoding)
default_separator = space
};
struct unit_symbol_formatting {
text_encoding encoding = text_encoding::default_encoding;
unit_symbol_solidus solidus = unit_symbol_solidus::default_denominator;
unit_symbol_separator separator = unit_symbol_separator::default_separator;
};
namespace detail {
// TODO Should `basic_symbol_text` be fixed to use `char` type for both encodings?
template<typename CharT, typename UnicodeCharT, std::size_t N, std::size_t M, std::output_iterator<CharT> Out>
constexpr Out copy(const basic_symbol_text<UnicodeCharT, N, M>& txt, text_encoding encoding, Out out)
{
if (encoding == text_encoding::unicode) {
if (is_same_v<CharT, UnicodeCharT>)
return copy(txt.unicode(), out).out;
else
throw std::invalid_argument("Unicode text can't be copied to CharT output");
} else {
if (is_same_v<CharT, char>)
return copy(txt.ascii(), out).out;
else
throw std::invalid_argument("ASCII text can't be copied to CharT output");
}
}
template<typename CharT, std::output_iterator<CharT> Out>
constexpr Out print_separator(Out out, unit_symbol_formatting fmt)
{
if (fmt.separator == unit_symbol_separator::dot) {
if (fmt.encoding != text_encoding::unicode)
throw std::invalid_argument("'unit_symbol_separator::dot' can be only used with 'text_encoding::unicode'");
copy(std::string_view("⋅"), out);
} else {
*out++ = ' ';
}
return out;
}
template<Unit U>
[[nodiscard]] consteval bool has_unit_symbol(U)
{
return !std::derived_from<U, derived_unit<>>;
}
template<typename CharT, std::output_iterator<CharT> Out, Unit U>
requires requires { U::symbol; }
constexpr Out unit_symbol_impl(Out out, U, unit_symbol_formatting fmt, bool negative_power)
{
out = copy<CharT>(U::symbol, fmt.encoding, out);
if (negative_power) {
constexpr auto txt = superscript<-1>();
out = copy<CharT>(txt, fmt.encoding, out);
}
return out;
}
template<typename CharT, std::output_iterator<CharT> Out, auto M, typename U>
constexpr Out unit_symbol_impl(Out out, const scaled_unit<M, U>& u, unit_symbol_formatting fmt, bool negative_power)
{
if constexpr (M == mag<1>) {
// no ratio/prefix
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
} else {
constexpr auto mag_txt = magnitude_text<M>();
out = copy<CharT>(mag_txt, fmt.encoding, out);
if constexpr (has_unit_symbol(u.reference_unit)) {
*out++ = ' ';
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
} else {
return out;
}
}
}
template<typename CharT, std::output_iterator<CharT> Out, typename F, int Num, int... Den>
constexpr auto unit_symbol_impl(Out out, const power<F, Num, Den...>&, unit_symbol_formatting fmt, bool negative_power)
{
out = unit_symbol_impl<CharT>(out, F{}, fmt, false); // negative power component will be added below if needed
constexpr ratio r = power<F, Num, Den...>::exponent;
if constexpr (r.den != 1) {
// add root part
constexpr auto txt = basic_fixed_string("^(") + regular<r.num>() + basic_fixed_string("/") + regular<r.den>() +
basic_fixed_string(")");
return copy<CharT>(txt, fmt.encoding, out);
} else if constexpr (r.num != 1) {
// add exponent part
if (negative_power) {
constexpr auto txt = superscript<-r.num>();
return copy<CharT>(txt, fmt.encoding, out);
} else {
constexpr auto txt = superscript<r.num>();
return copy<CharT>(txt, fmt.encoding, out);
}
}
}
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr M>
constexpr Out unit_symbol_impl(Out out, M m, std::size_t Idx, unit_symbol_formatting fmt, bool negative_power)
{
if (Idx > 0) out = print_separator<CharT>(out, fmt);
return unit_symbol_impl<CharT>(out, m, fmt, negative_power);
}
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr... Ms, std::size_t... Idxs>
constexpr Out unit_symbol_impl(Out out, const type_list<Ms...>&, std::index_sequence<Idxs...>,
unit_symbol_formatting fmt, bool negative_power)
{
return (..., (out = unit_symbol_impl<CharT>(out, Ms{}, Idxs, fmt, negative_power)));
}
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr... Nums, DerivedUnitExpr... Dens>
constexpr Out unit_symbol_impl(Out out, const type_list<Nums...>& nums, const type_list<Dens...>& dens,
unit_symbol_formatting fmt)
{
if constexpr (sizeof...(Nums) == 0 && sizeof...(Dens) == 0) {
// dimensionless quantity
return out;
} else if constexpr (sizeof...(Dens) == 0) {
// no denominator
return unit_symbol_impl<CharT>(out, nums, std::index_sequence_for<Nums...>(), fmt, false);
} else {
using enum unit_symbol_solidus;
if constexpr (sizeof...(Nums) > 0) {
unit_symbol_impl<CharT>(out, nums, std::index_sequence_for<Nums...>(), fmt, false);
}
if (fmt.solidus == always || (fmt.solidus == one_denominator && sizeof...(Dens) == 1)) {
if constexpr (sizeof...(Nums) == 0) *out++ = '1';
*out++ = '/';
} else {
out = print_separator<CharT>(out, fmt);
}
if (fmt.solidus == always && sizeof...(Dens) > 1) *out++ = '(';
bool negative_power = fmt.solidus == never || (fmt.solidus == one_denominator && sizeof...(Dens) > 1);
out = unit_symbol_impl<CharT>(out, dens, std::index_sequence_for<Dens...>(), fmt, negative_power);
if (fmt.solidus == always && sizeof...(Dens) > 1) *out++ = ')';
return out;
}
}
template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, unit_symbol_formatting fmt, bool negative_power)
{
gsl_Expects(negative_power == false);
return unit_symbol_impl<CharT>(out, typename derived_unit<Expr...>::_num_{}, typename derived_unit<Expr...>::_den_{},
fmt);
}
} // namespace detail
template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>
constexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{})
{
return detail::unit_symbol_impl<CharT>(out, u, fmt, false);
}
template<typename CharT = char, Unit U>
[[nodiscard]] constexpr std::basic_string<CharT> unit_symbol(U u, unit_symbol_formatting fmt = unit_symbol_formatting{})
{
std::basic_string<CharT> buffer;
unit_symbol_to<CharT>(std::back_inserter(buffer), u, fmt);
return buffer;
}
} // namespace mp_units
-66
View File
@@ -1,66 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/bits/basic_concepts.h>
#include <units/bits/external/fixed_string.h>
// IWYU pragma: end_exports
#include <type_traits>
namespace units {
/**
* @brief A dimension of a base quantity
*
* Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity
* in the subset can be expressed in terms of the other quantities within that subset. They are referred to as
* being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
* quantities.
*
* Base unit is a measurement unit that is adopted by convention for a base quantity in a specific system of units.
*
* Pair of Symbol and Unit template parameters form an unique identifier of the base dimension. The same identifiers can
* be multiplied and divided which will result with an adjustment of its factor in an Exponent of a DerivedDimension
* (in case of zero the dimension will be simplified and removed from further analysis of current expresion). In case
* the Symbol is the same but the Unit differs (i.e. mixing SI and CGS length), there is no automatic simplification but
* is possible to force it with a quantity_cast.
*
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
* @tparam U a base unit to be used for this base dimension
*/
template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
using base_unit = U; ///< Base unit adopted for this dimension
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = magnitude{};
};
// base_dimension_less
template<BaseDimension D1, BaseDimension D2>
struct base_dimension_less :
std::bool_constant<(D1::symbol < D2::symbol) ||
(D1::symbol == D2::symbol && D1::base_unit::symbol < D1::base_unit::symbol)> {};
} // namespace units
@@ -1,48 +0,0 @@
// 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 <units/base_dimension.h>
#include <units/exponent.h>
#include <units/magnitude.h>
#include <units/ratio.h>
namespace units::detail {
/**
* @brief Calculates the "absolute" magnitude of the derived dimension defined by this list.
*
* "Absolute" magnitudes are not physically observable: only ratios of magnitudes are. For example: if we multiplied
* all magnitudes in the system by the same constant, no meaningful results would change. However, in practice, we need
* to make some global choice for the "absolute" values of magnitudes, so that we can compute their ratios.
*
* The point of this function is to compute the absolute magnitude of a derived dimension, in terms of the absolute
* magnitudes of its constituent dimensions.
*/
template<typename... Es>
constexpr Magnitude auto absolute_magnitude(exponent_list<Es...>)
{
return (magnitude<>{} * ... * pow<ratio{Es::num, Es::den}>(Es::dimension::base_unit::mag));
}
} // namespace units::detail
@@ -1,489 +0,0 @@
// 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 <units/bits/external/hacks.h>
// IWYU pragma: begin_exports
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/type_traits.h>
#include <units/customization_points.h>
#include <units/magnitude.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
#include <cstdint>
#include <functional>
#include <utility>
namespace units {
// Prefix
namespace detail {
template<Magnitude auto M>
struct prefix_base;
template<Magnitude auto M>
void to_prefix_base(const volatile prefix_base<M>*);
} // namespace detail
/**
* @brief A concept matching a symbol prefix
*
* Satisfied by all specializations of `prefix`.
*/
template<typename T>
concept Prefix = requires(T* t) { detail::to_prefix_base(t); };
// Unit
template<Magnitude auto M, typename U>
struct scaled_unit;
// TODO: Remove when P1985 accepted
namespace detail {
template<Magnitude auto M, typename U>
void to_base_scaled_unit(const volatile scaled_unit<M, U>*);
} // namespace detail
/**
* @brief A concept matching all unit types in the library
*
* Satisfied by all unit types derived from an specialization of :class:`scaled_unit`.
*/
template<typename T>
concept Unit = requires(T* t) { detail::to_base_scaled_unit(t); };
namespace detail {
template<typename>
inline constexpr bool is_named = false;
}
template<typename T>
concept NamedUnit = Unit<T> && detail::is_named<T>;
template<Unit U, basic_symbol_text Symbol>
struct alias_unit;
// TODO: Remove when P1985 accepted
namespace detail {
template<Unit U, basic_symbol_text Symbol>
void to_base_alias_unit(const volatile alias_unit<U, Symbol>*);
} // namespace detail
template<typename T>
concept AliasUnit = requires(T* t) { detail::to_base_alias_unit(t); };
// BaseDimension
template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension;
namespace detail {
template<basic_fixed_string Symbol, typename U>
void to_base_base_dimension(const volatile base_dimension<Symbol, U>*);
} // namespace detail
/**
* @brief A concept matching all base dimensions in the library.
*
* Satisfied by all dimension types derived from an specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); };
// Exponent
namespace detail {
template<typename T>
inline constexpr bool is_exponent = false;
} // namespace detail
/**
* @brief A concept matching dimension's exponents.
*
* Satisfied by all specializations of :class:`exponent`.
*/
template<typename T>
concept Exponent = detail::is_exponent<T>;
// DerivedDimension
namespace detail {
template<Exponent... Es>
requires(BaseDimension<typename Es::dimension> && ...)
struct derived_dimension_base;
} // namespace detail
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types derived from an specialization of `detail::derived_dimension_base`.
*/
template<typename T>
concept DerivedDimension = is_derived_from_specialization_of<T, detail::derived_dimension_base>;
// Dimension
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
// UnitOf
namespace detail {
template<Dimension D>
auto default_unit()
{
if constexpr (BaseDimension<D>)
return typename D::base_unit{};
else
return typename D::coherent_unit{};
}
} // namespace detail
/**
* @brief Returns a 'default' unit of the dimension
*
* Depending on the dimension type it returns a base unit (for base dimensions)
* or a coherent unit (in case of derived dimensions).
*
* @tparam D Dimension type to get the unit from.
*/
template<Dimension D>
using dimension_unit = decltype(detail::default_unit<D>());
// TODO: replace with the below code when VS catches up
// decltype([]{
// if constexpr (BaseDimension<D>)
// return TYPENAME D::base_unit{};
// else
// return TYPENAME D::coherent_unit{};
// }());
/**
* @brief A concept matching only units of a specified dimension.
*
* Satisfied by all unit types that satisfy `Unit<U>`, `Dimension<D>`, and for which
* `U::reference` and @c dimension_unit<D>::reference denote the same unit type.
*
* @tparam U Type to verify.
* @tparam D Dimension type to use for verification.
*/
template<typename U, typename D>
concept UnitOf = Unit<U> && Dimension<D> && std::same_as<typename U::reference, typename dimension_unit<D>::reference>;
// PointOrigin
template<Dimension D>
struct point_origin;
/**
* @brief A concept matching a point origin
*
* Satisfied by types derived from an specialization of @c point_origin.
*/
template<typename T>
concept PointOrigin = is_derived_from_specialization_of<T, point_origin> && requires {
typename T::dimension;
requires Dimension<typename T::dimension>;
typename T::point_origin;
requires std::same_as<typename T::point_origin, point_origin<typename T::dimension>>;
requires !std::same_as<T, point_origin<typename T::dimension>>;
};
// RebindablePointOriginFor
namespace detail {
template<typename O, typename D>
struct rebind_point_origin_dimension_impl {
using type = typename O::template rebind<D>;
};
} // namespace detail
template<PointOrigin O, Dimension D>
using rebind_point_origin_dimension = typename conditional<is_same_v<typename O::dimension, D>, std::type_identity<O>,
detail::rebind_point_origin_dimension_impl<O, D>>::type;
/**
* @brief A concept predicating the possibility of changing an origin's dimension
*
* Satisfied by point origins whose dimension can be made to be `D`.
*/
template<typename T, typename D>
concept RebindablePointOriginFor =
requires { typename rebind_point_origin_dimension<T, D>; } && PointOrigin<rebind_point_origin_dimension<T, D>> &&
std::same_as<D, typename rebind_point_origin_dimension<T, D>::dimension>;
// Kind
namespace detail {
template<typename, Dimension>
struct _kind_base;
} // namespace detail
template<typename T, template<typename...> typename Base>
concept kind_impl_ = is_derived_from_specialization_of<T, Base> && requires {
typename T::base_kind;
typename T::dimension;
requires Dimension<typename T::dimension>;
};
/**
* @brief A concept matching all kind types
*
* Satisfied by all kind types derived from an specialization of @c kind.
*/
template<typename T>
concept Kind = kind_impl_<T, detail::_kind_base> && kind_impl_<typename T::base_kind, detail::_kind_base> &&
std::same_as<typename T::base_kind, typename T::base_kind::base_kind>;
// PointKind
namespace detail {
template<Kind, PointOrigin>
struct _point_kind_base;
} // namespace detail
/**
* @brief A concept matching all point kind types
*
* Satisfied by all point kind types derived from an specialization of @c point_kind.
*/
template<typename T>
concept PointKind =
kind_impl_<T, detail::_point_kind_base> && requires { typename T::origin; } && PointOrigin<typename T::origin> &&
std::same_as<typename T::dimension, typename T::base_kind::dimension> &&
std::same_as<typename T::dimension, typename T::origin::dimension>;
// Reference
namespace detail {
template<typename T>
inline constexpr bool is_reference = false;
} // namespace detail
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = detail::is_reference<T>;
// Quantity, QuantityPoint, QuantityKind, QuantityPointKind
namespace detail {
template<typename T>
inline constexpr bool is_quantity = false;
template<typename T>
inline constexpr bool is_quantity_point = false;
template<typename T>
inline constexpr bool is_quantity_kind = false;
template<typename T>
inline constexpr bool is_quantity_point_kind = false;
template<typename T>
inline constexpr bool is_quantity_like = false;
template<typename T>
inline constexpr bool is_quantity_point_like = false;
} // namespace detail
/**
* @brief A concept matching all quantities in the library.
*
* Satisfied by all specializations of @c quantity.
*/
template<typename T>
concept Quantity = detail::is_quantity<T>;
/**
* @brief A concept matching all quantity points in the library.
*
* Satisfied by all specializations of @c quantity_point.
*/
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
/**
* @brief A concept matching all quantity kinds in the library.
*
* Satisfied by all specializations of @c quantity_kind.
*/
template<typename T>
concept QuantityKind = detail::is_quantity_kind<T>;
/**
* @brief A concept matching all quantity point kinds in the library.
*
* Satisfied by all specializations of @c quantity_point_kind.
*/
template<typename T>
concept QuantityPointKind = detail::is_quantity_point_kind<T>;
// QuantityLike, QuantityPointLike
/**
* @brief A concept matching all quantity-like types (other than specialization of @c quantity)
*
* Satisfied by all types for which a correct specialization of `quantity_like_traits`
* type trait is provided.
*/
template<typename T>
concept QuantityLike = detail::is_quantity_like<T>;
/**
* @brief A concept matching all quantity point-like types (other than specialization of @c quantity_point)
*
* Satisfied by all types for which a correct specialization of `quantity_point_like_traits`
* type trait is provided.
*/
template<typename T>
concept QuantityPointLike = detail::is_quantity_point_like<T>;
// Representation
template<typename T, typename U>
concept common_type_with_ = // exposition only
(std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>>)&&(
std::constructible_from<std::common_type_t<T, U>, T>)&&(std::constructible_from<std::common_type_t<T, U>, U>);
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
(std::regular_invocable<std::multiplies<>, T, U>)&&(std::regular_invocable<std::divides<>, T, U>);
template<typename T>
concept castable_number_ = // exposition only
common_type_with_<T, std::intmax_t> && scalable_number_<std::common_type_t<T, std::intmax_t>>;
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> ||
(requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>) ||
(requires { typename T::element_type; } && castable_number_<typename T::element_type> &&
scalable_number_<T, std::common_type_t<typename T::element_type, std::intmax_t>>);
template<typename T, typename U>
concept scalable_with_ = // exposition only
common_type_with_<T, U> && scalable_<std::common_type_t<T, U>>;
// WrappedQuantity
namespace detail {
template<typename T>
inline constexpr bool is_wrapped_quantity = false;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool is_wrapped_quantity<T> =
Quantity<typename T::value_type> || QuantityLike<typename T::value_type> ||
is_wrapped_quantity<typename T::value_type>;
template<typename T>
requires requires { typename T::element_type; }
inline constexpr bool is_wrapped_quantity<T> =
Quantity<typename T::element_type> || QuantityLike<typename T::element_type> ||
is_wrapped_quantity<typename T::element_type>;
template<typename T>
requires requires { typename T::quantity_type; }
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::quantity_type>;
} // namespace detail
/**
* @brief A concept matching types that wrap quantity objects.
*
* Satisfied by all wrapper types that satisfy `Quantity<typename T::value_type>` or
* `Quantity<typename T::element_type>` recursively
* (i.e. `std::optional<si::length<si::metre>>`).
*/
template<typename T>
concept wrapped_quantity_ = // exposition only
detail::is_wrapped_quantity<T>;
/**
* @brief A concept matching non-Quantity types.
*
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
*/
template<typename T>
concept Representation = (!Quantity<T>)&&(!QuantityLike<T>)&&(!wrapped_quantity_<T>)&&std::regular<T> && scalable_<T>;
namespace detail {
template<typename T>
requires requires(T q) {
typename quantity_like_traits<T>::dimension;
typename quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<typename quantity_like_traits<T>::dimension>;
requires Unit<typename quantity_like_traits<T>::unit>;
requires Representation<typename quantity_like_traits<T>::rep>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
}
inline constexpr bool is_quantity_like<T> = true;
template<typename T>
requires requires(T q) {
typename quantity_point_like_traits<T>::dimension;
typename quantity_point_like_traits<T>::unit;
typename quantity_point_like_traits<T>::rep;
requires Dimension<typename quantity_point_like_traits<T>::dimension>;
requires Unit<typename quantity_point_like_traits<T>::unit>;
requires Representation<typename quantity_point_like_traits<T>::rep>;
{
quantity_point_like_traits<T>::relative(q)
} -> QuantityLike;
}
inline constexpr bool is_quantity_point_like<T> = true;
} // namespace detail
} // namespace units
-125
View File
@@ -1,125 +0,0 @@
// 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 <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
#include <units/quantity_cast.h>
namespace units {
template<Dimension D, UnitOf<D> U>
struct reference;
template<Dimension D, UnitOf<D> U, Representation Rep>
class quantity;
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
class quantity_point;
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
class quantity_kind;
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
class quantity_point_kind;
namespace detail {
template<typename R1, typename R2>
struct common_quantity_reference_impl;
template<typename D, typename U>
struct common_quantity_reference_impl<reference<D, U>, reference<D, U>> {
using type = reference<D, U>;
};
template<typename D, typename U1, typename U2>
struct common_quantity_reference_impl<reference<D, U1>, reference<D, U2>> {
using type = reference<D, downcast_unit<D, common_magnitude(U1::mag, U2::mag)>>;
};
template<typename D1, typename U1, typename D2, typename U2>
requires(same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value)
struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
using type = reference<D1, downcast_unit<D1, common_magnitude(U1::mag, U2::mag)>>;
};
template<typename D1, typename U1, typename D2, typename U2>
struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = common_magnitude(reference<D1, U1>::mag,
reference<D2, U2>::mag);
using dimension = conditional<is_specialization_of<D1, unknown_dimension>, D2, D1>;
using unit = downcast_unit<dimension, mag / dimension::mag>;
using type = reference<dimension, unit>;
};
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
using common_quantity_reference =
TYPENAME detail::common_quantity_reference_impl<std::remove_const_t<decltype(Q1::reference)>,
std::remove_const_t<decltype(Q2::reference)>>::type;
} // namespace detail
} // namespace units
namespace std {
template<units::Quantity Q1, units::QuantityEquivalentTo<Q1> Q2>
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
struct common_type<Q1, Q2> {
private:
using ref = units::detail::common_quantity_reference<Q1, Q2>;
public:
using type =
units::quantity<typename ref::dimension, typename ref::unit, common_type_t<typename Q1::rep, typename Q2::rep>>;
};
template<units::QuantityPoint QP1, units::QuantityPointEquivalentTo<QP1> QP2>
requires requires { typename common_type_t<typename QP1::rep, typename QP2::rep>; }
struct common_type<QP1, QP2> {
using type =
units::quantity_point<units::rebind_point_origin_dimension<
typename QP1::origin, typename common_type_t<typename QP1::quantity_type,
typename QP2::quantity_type>::dimension>,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::unit,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::rep>;
};
template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
struct common_type<QK1, QK2> {
using type =
units::quantity_kind<typename QK1::kind_type,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
};
template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
struct common_type<QPK1, QPK2> {
using type = units::quantity_point_kind<
typename QPK1::point_kind_type,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
};
} // namespace std
@@ -1,244 +0,0 @@
// 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 <gsl/gsl-lite.hpp>
#include <units/bits/external/hacks.h>
#include <units/bits/math_concepts.h>
#include <units/bits/ratio_maths.h>
namespace units::detail {
struct decimal_fp {
double significant;
std::intmax_t exponent;
};
[[nodiscard]] constexpr decimal_fp to_decimal(double v) noexcept
{
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
if (v == 0) {
return {.significant = 0.0, .exponent = 0};
}
UNITS_DIAGNOSTIC_POP
double significant = abs(v);
std::intmax_t exponent = 0;
while (significant < 1) {
significant *= 10.0;
--exponent;
}
while (significant >= 10) {
significant /= 10.0;
++exponent;
}
if (v < 0) {
significant = -significant;
}
return {.significant = significant, .exponent = exponent};
}
/* approximate natural log as https://math.stackexchange.com/a/977836
far slower than std::log but works at compile time with similar accuracy
*/
[[nodiscard]] constexpr double constexpr_log(double v) noexcept
{
gsl_Expects(v > 0);
// lookup table to speed up convergence for all significant values
// significant values of 7 and greater benefit mostly as they now converge in 5 terms compared to O(10)-O(100)
// required without the table
//
// using python:
// >>> import math
// >>> for i in range(1, 100):
// ... print(f"/* log({i:>2d}) = */ {math.log(i):.16f},")
constexpr std::array<double, 99> log_table{
/* log( 1) = */ 0.0000000000000000,
/* log( 2) = */ 0.6931471805599453,
/* log( 3) = */ 1.0986122886681098,
/* log( 4) = */ 1.3862943611198906,
/* log( 5) = */ 1.6094379124341003,
/* log( 6) = */ 1.7917594692280550,
/* log( 7) = */ 1.9459101490553132,
/* log( 8) = */ 2.0794415416798357,
/* log( 9) = */ 2.1972245773362196,
/* log(10) = */ 2.3025850929940459,
/* log(11) = */ 2.3978952727983707,
/* log(12) = */ 2.4849066497880004,
/* log(13) = */ 2.5649493574615367,
/* log(14) = */ 2.6390573296152584,
/* log(15) = */ 2.7080502011022101,
/* log(16) = */ 2.7725887222397811,
/* log(17) = */ 2.8332133440562162,
/* log(18) = */ 2.8903717578961645,
/* log(19) = */ 2.9444389791664403,
/* log(20) = */ 2.9957322735539909,
/* log(21) = */ 3.0445224377234230,
/* log(22) = */ 3.0910424533583161,
/* log(23) = */ 3.1354942159291497,
/* log(24) = */ 3.1780538303479458,
/* log(25) = */ 3.2188758248682006,
/* log(26) = */ 3.2580965380214821,
/* log(27) = */ 3.2958368660043291,
/* log(28) = */ 3.3322045101752038,
/* log(29) = */ 3.3672958299864741,
/* log(30) = */ 3.4011973816621555,
/* log(31) = */ 3.4339872044851463,
/* log(32) = */ 3.4657359027997265,
/* log(33) = */ 3.4965075614664802,
/* log(34) = */ 3.5263605246161616,
/* log(35) = */ 3.5553480614894135,
/* log(36) = */ 3.5835189384561099,
/* log(37) = */ 3.6109179126442243,
/* log(38) = */ 3.6375861597263857,
/* log(39) = */ 3.6635616461296463,
/* log(40) = */ 3.6888794541139363,
/* log(41) = */ 3.7135720667043080,
/* log(42) = */ 3.7376696182833684,
/* log(43) = */ 3.7612001156935624,
/* log(44) = */ 3.7841896339182610,
/* log(45) = */ 3.8066624897703196,
/* log(46) = */ 3.8286413964890951,
/* log(47) = */ 3.8501476017100584,
/* log(48) = */ 3.8712010109078911,
/* log(49) = */ 3.8918202981106265,
/* log(50) = */ 3.9120230054281460,
/* log(51) = */ 3.9318256327243257,
/* log(52) = */ 3.9512437185814275,
/* log(53) = */ 3.9702919135521220,
/* log(54) = */ 3.9889840465642745,
/* log(55) = */ 4.0073331852324712,
/* log(56) = */ 4.0253516907351496,
/* log(57) = */ 4.0430512678345503,
/* log(58) = */ 4.0604430105464191,
/* log(59) = */ 4.0775374439057197,
/* log(60) = */ 4.0943445622221004,
/* log(61) = */ 4.1108738641733114,
/* log(62) = */ 4.1271343850450917,
/* log(63) = */ 4.1431347263915326,
/* log(64) = */ 4.1588830833596715,
/* log(65) = */ 4.1743872698956368,
/* log(66) = */ 4.1896547420264252,
/* log(67) = */ 4.2046926193909657,
/* log(68) = */ 4.2195077051761070,
/* log(69) = */ 4.2341065045972597,
/* log(70) = */ 4.2484952420493594,
/* log(71) = */ 4.2626798770413155,
/* log(72) = */ 4.2766661190160553,
/* log(73) = */ 4.2904594411483910,
/* log(74) = */ 4.3040650932041702,
/* log(75) = */ 4.3174881135363101,
/* log(76) = */ 4.3307333402863311,
/* log(77) = */ 4.3438054218536841,
/* log(78) = */ 4.3567088266895917,
/* log(79) = */ 4.3694478524670215,
/* log(80) = */ 4.3820266346738812,
/* log(81) = */ 4.3944491546724391,
/* log(82) = */ 4.4067192472642533,
/* log(83) = */ 4.4188406077965983,
/* log(84) = */ 4.4308167988433134,
/* log(85) = */ 4.4426512564903167,
/* log(86) = */ 4.4543472962535073,
/* log(87) = */ 4.4659081186545837,
/* log(88) = */ 4.4773368144782069,
/* log(89) = */ 4.4886363697321396,
/* log(90) = */ 4.4998096703302650,
/* log(91) = */ 4.5108595065168497,
/* log(92) = */ 4.5217885770490405,
/* log(93) = */ 4.5325994931532563,
/* log(94) = */ 4.5432947822700038,
/* log(95) = */ 4.5538768916005408,
/* log(96) = */ 4.5643481914678361,
/* log(97) = */ 4.5747109785033828,
/* log(98) = */ 4.5849674786705723,
/* log(99) = */ 4.5951198501345898,
};
decimal_fp x = to_decimal(v);
// dividing the significant by nearest lower value in [1.0, 1.1, 1.2, ..., 9.9] will greatly improve convergence
x.significant *= 10;
const auto isignificant = static_cast<std::size_t>(x.significant);
x.significant /= static_cast<double>(isignificant);
const double result = static_cast<double>(x.exponent - 1) * log_table[9] + log_table[isignificant - 1];
// 1.0 <= significant < 1.1 converges rapidly
const double y = (x.significant - 1) / (x.significant + 1);
const double y_squared = y * y;
double sum = 0;
// 5 terms are needed for convergence to machine precision in the worst case scenario
for (int k = 4; k > 0; --k) {
sum = y_squared * (1 / (2 * static_cast<double>(k) + 1) + sum);
}
sum = 2 * y * (1 + sum); // k = 0 term
return result + sum;
}
/* approximate e^x as Taylor series e^x = 1 + x/1! + x^2/2! + x^3/3! +... where N is the order of the Taylor series
use https://math.stackexchange.com/a/1988927 to improve convergence for large values
larger Factor values improve convergence for all values but reduce the precision
*/
template<std::size_t N, std::intmax_t Factor = 256>
requires gt_zero<Factor>
[[nodiscard]] constexpr double constexpr_exp(double v) noexcept
{
if constexpr (N == 0) {
return 1.0;
} else {
constexpr auto coefficients = []() {
std::array<double, N> coeffs;
std::size_t factorial = 1;
for (std::size_t i = 0; i < N; ++i) {
factorial *= i + 1;
coeffs[i] = 1.0 / static_cast<double>(factorial);
}
return coeffs;
}();
const double x = v / static_cast<double>(Factor);
double result = 0;
for (auto i = static_cast<std::intmax_t>(N - 1); i >= 0; --i) {
result = x * (coefficients[static_cast<std::size_t>(i)] + result);
}
// for factors of power of 2 this should be replaced by log2(Factor) multiplications by the compiler
return pow_impl<Factor>(1 + result);
}
}
// default template arguments provide reasonable precision even for fairly large exponents
// see constexpr_exp for template arguments
template<std::size_t ExpOrder = 10, std::intmax_t Factor = 128>
[[nodiscard]] constexpr double constexpr_pow(double v, double exponent) noexcept
{
const double x = exponent * constexpr_log(v);
return constexpr_exp<ExpOrder, Factor>(x);
}
} // namespace units::detail
@@ -1,60 +0,0 @@
// 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 <units/base_dimension.h>
#include <units/bits/external/downcasting.h>
#include <units/exponent.h>
namespace units::detail {
/**
* @brief A dimension of a derived quantity
*
* Expression of the dependence of a quantity on the base quantities (and their base dimensions) of a system of
* quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors.
* A power of a factor is the factor raised to an exponent.
*
* A derived dimension can be formed from multiple exponents (i.e. speed is represented as "exponent<L, 1>, exponent<T,
* -1>"). It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just
* "exponent<T, -1>").
*
* @note This class template is used by the library engine and should not be directly instantiated by the user.
*
* @tparam Es zero or more exponents of a derived dimension
*/
template<Exponent... Es>
requires(BaseDimension<typename Es::dimension> && ...)
struct derived_dimension_base : downcast_base<derived_dimension_base<Es...>> {
using exponents = exponent_list<Es...>;
};
template<typename T>
struct to_derived_dimension_base;
template<Exponent... Es>
struct to_derived_dimension_base<exponent_list<Es...>> {
using type = derived_dimension_base<Es...>;
};
} // namespace units::detail
@@ -1,48 +0,0 @@
// 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 <units/derived_dimension.h>
#include <units/magnitude.h>
namespace units::detail {
// compatible_units
template<typename ExpList, Unit... Us>
inline constexpr bool compatible_units = false;
template<typename... Es, Unit... Us>
inline constexpr bool compatible_units<exponent_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...);
// derived_scaled_unit
template<Unit... Us, typename... Es>
constexpr Magnitude auto derived_mag(exponent_list<Es...>)
{
return (magnitude<>{} * ... * pow<ratio{Es::num, Es::den}>(Us::mag / dimension_unit<typename Es::dimension>::mag));
}
template<DerivedDimension D, Unit... Us>
using derived_scaled_unit = scaled_unit<derived_mag<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
} // namespace units::detail
@@ -1,89 +0,0 @@
// 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 <units/bits/external/fixed_string.h>
#include <units/bits/external/text_tools.h>
#include <units/derived_dimension.h>
#include <units/symbol_text.h>
namespace units::detail {
template<bool Divide, std::size_t NegativeExpCount, std::size_t Idx>
constexpr auto operator_text()
{
if constexpr (Idx == 0) {
if constexpr (Divide && NegativeExpCount == 1) {
return basic_fixed_string("1/");
} else {
return basic_fixed_string("");
}
} else {
if constexpr (Divide && NegativeExpCount == 1) {
return basic_fixed_string("/");
} else {
return basic_symbol_text("⋅", " ");
}
}
}
template<typename E, basic_symbol_text Symbol, std::size_t NegativeExpCount, std::size_t Idx>
constexpr auto exp_text()
{
// get calculation operator + symbol
const auto txt = operator_text<(E::num < 0), NegativeExpCount, Idx>() + Symbol;
if constexpr (E::den != 1) {
// add root part
return txt + basic_fixed_string("^(") + regular<abs(E::num)>() + basic_fixed_string("/") + regular<E::den>() +
basic_fixed_string(")");
} else if constexpr (E::num != 1) {
// add exponent part
if constexpr (NegativeExpCount > 1) { // no '/' sign here (only negative exponents)
return txt + superscript<E::num>();
} else if constexpr (E::num != -1) { // -1 is replaced with '/' sign here
return txt + superscript<abs(E::num)>();
} else {
return txt;
}
} else {
return txt;
}
}
template<typename... Es>
inline constexpr int negative_exp_count = ((Es::num < 0 ? 1 : 0) + ... + 0);
template<typename... Us, typename... Es, std::size_t... Idxs>
constexpr auto derived_symbol_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
{
constexpr auto neg_exp = negative_exp_count<Es...>;
return (exp_text<Es, Us::symbol, neg_exp, Idxs>() + ...);
}
template<DerivedDimension Dim, Unit... Us>
constexpr auto derived_symbol_text()
{
return derived_symbol_text<Us...>(typename Dim::recipe(), std::index_sequence_for<Us...>());
}
} // namespace units::detail
@@ -1,67 +0,0 @@
// 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 <units/bits/external/type_list.h>
#include <units/exponent.h>
#include <ratio>
namespace units::detail {
/**
* @brief Consolidates contiguous ranges of exponents of the same dimension
*
* If there is more than one exponent with the same dimension they are aggregated into one exponent by adding
* their exponents. If this accumulation will result with 0, such a dimension is removed from the list.
*
* @tparam D derived dimension to consolidate
*/
template<typename ExpList>
struct dim_consolidate;
template<>
struct dim_consolidate<exponent_list<>> {
using type = exponent_list<>;
};
template<typename E>
struct dim_consolidate<exponent_list<E>> {
using type = exponent_list<E>;
};
template<typename E1, typename... ERest>
struct dim_consolidate<exponent_list<E1, ERest...>> {
using type = type_list_push_front<typename dim_consolidate<exponent_list<ERest...>>::type, E1>;
};
template<BaseDimension Dim, std::intmax_t Num1, std::intmax_t Den1, std::intmax_t Num2, std::intmax_t Den2,
typename... ERest>
struct dim_consolidate<exponent_list<exponent<Dim, Num1, Den1>, exponent<Dim, Num2, Den2>, ERest...>> {
using r1 = std::ratio<Num1, Den1>;
using r2 = std::ratio<Num2, Den2>;
using r = std::ratio_add<r1, r2>;
using type = conditional<r::num == 0, typename dim_consolidate<exponent_list<ERest...>>::type,
typename dim_consolidate<exponent_list<exponent<Dim, r::num, r::den>, ERest...>>::type>;
};
} // namespace units::detail
-59
View File
@@ -1,59 +0,0 @@
// 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 <units/bits/derived_dimension_base.h>
#include <units/bits/external/type_list.h>
#include <units/exponent.h>
namespace units::detail {
/**
* @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions
*
* @tparam Es Exponents of potentially derived dimensions
*/
template<Exponent... Es>
struct dim_unpack;
template<>
struct dim_unpack<> {
using type = exponent_list<>;
};
template<BaseDimension Dim, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<Dim, Num, Den>, ERest...> {
using type = type_list_push_front<typename dim_unpack<ERest...>::type, exponent<Dim, Num, Den>>;
};
template<DerivedDimension Dim, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<Dim, Num, Den>, ERest...> {
using type = TYPENAME dim_unpack<exponent<downcast_base_t<Dim>, Num, Den>, ERest...>::type;
};
template<Exponent... Es, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<derived_dimension_base<Es...>, Num, Den>, ERest...> {
using type = type_list_push_front<typename dim_unpack<ERest...>::type, exponent_multiply<Es, Num, Den>...>;
};
} // namespace units::detail
-208
View File
@@ -1,208 +0,0 @@
// 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 <units/bits/derived_dimension_base.h>
#include <units/bits/dim_consolidate.h>
#include <units/bits/external/downcasting.h>
#include <units/bits/external/hacks.h>
#include <units/bits/external/type_list.h>
#include <units/derived_dimension.h>
#include <units/unit.h>
namespace units {
/**
* @brief Unknown dimension
*
* Sometimes a temporary partial result of a complex calculation may not result in a predefined
* dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit`
* with a magnitude being the absolute one of all the exponents of such a dimension.
*
* @tparam Es the list of exponents of ingredient dimensions
*/
template<Exponent... Es>
struct unknown_dimension : derived_dimension<unknown_dimension<Es...>, unknown_coherent_unit<Es...>, Es...> {};
namespace detail {
template<DerivedDimension D>
struct check_unknown {
using type = D;
};
// downcast did not find a user predefined type
template<typename... Es>
struct check_unknown<derived_dimension_base<Es...>> {
using type = unknown_dimension<Es...>;
};
template<Dimension D>
struct downcast_dimension_impl;
template<BaseDimension D>
struct downcast_dimension_impl<D> {
using type = D;
};
template<DerivedDimension D>
struct downcast_dimension_impl<D> {
using type = TYPENAME check_unknown<downcast<D>>::type;
};
} // namespace detail
template<Dimension D>
using downcast_dimension = TYPENAME detail::downcast_dimension_impl<D>::type;
// dim_invert
namespace detail {
template<Dimension D>
struct dim_invert_impl;
template<BaseDimension D>
struct dim_invert_impl<D> {
using type = downcast_dimension<derived_dimension_base<exponent<D, -1>>>;
};
template<BaseDimension D>
struct dim_invert_impl<derived_dimension_base<exponent<D, -1>>> {
using type = D;
};
template<typename... Es>
struct dim_invert_impl<derived_dimension_base<Es...>> {
using type = downcast_dimension<derived_dimension_base<exponent_invert<Es>...>>;
};
template<DerivedDimension D>
struct dim_invert_impl<D> : dim_invert_impl<downcast_base_t<D>> {};
} // namespace detail
template<Dimension D>
using dim_invert = TYPENAME detail::dim_invert_impl<D>::type;
// dimension_multiply
namespace detail {
template<typename T>
struct to_dimension;
template<Exponent... Es>
struct to_dimension<exponent_list<Es...>> {
using type = derived_dimension_base<Es...>;
};
template<BaseDimension D>
struct to_dimension<exponent_list<exponent<D, 1>>> {
using type = D;
};
/**
* @brief Merges 2 sorted derived dimensions into one units::derived_dimension_base
*
* A result of a dimensional calculation may result with many exponents of the same base dimension orginated
* from different parts of the equation. As the exponents lists of both operands it is enough to merge them
* into one list and consolidate duplicates. Also it is possible that final exponents list will contain only
* one element being a base dimension with exponent 1. In such a case the final dimension should be the base
* dimension itself.
*/
template<Dimension D1, Dimension D2>
using merge_dimension = TYPENAME to_dimension<typename dim_consolidate<
type_list_merge_sorted<typename D1::exponents, typename D2::exponents, exponent_less>>::type>::type;
template<Dimension D1, Dimension D2>
struct dimension_multiply_impl;
template<BaseDimension D1, BaseDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = downcast_dimension<
merge_dimension<derived_dimension_base<exponent<D1, 1>>, derived_dimension_base<exponent<D2, 1>>>>;
};
template<BaseDimension D1, DerivedDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type =
downcast_dimension<merge_dimension<derived_dimension_base<exponent<D1, 1>>, typename D2::downcast_base_type>>;
};
template<DerivedDimension D1, BaseDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = TYPENAME dimension_multiply_impl<D2, D1>::type;
};
template<DerivedDimension D1, DerivedDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = downcast_dimension<merge_dimension<typename D1::downcast_base_type, typename D2::downcast_base_type>>;
};
} // namespace detail
template<Dimension D1, Dimension D2>
using dimension_multiply = TYPENAME detail::dimension_multiply_impl<D1, D2>::type;
template<Dimension D1, Dimension D2>
using dimension_divide = TYPENAME detail::dimension_multiply_impl<D1, dim_invert<D2>>::type;
// dimension_pow
namespace detail {
template<Dimension D, std::intmax_t Num, std::intmax_t Den = 1>
struct dimension_pow_impl;
template<BaseDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<D, Num, Den> {
using type = downcast_dimension<derived_dimension_base<exponent<D, Num, Den>>>;
};
template<BaseDimension D>
struct dimension_pow_impl<D, 1, 1> {
using type = D;
};
template<BaseDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<derived_dimension_base<exponent<D, Den, Num>>, Num, Den> {
using type = D;
};
template<DerivedDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<D, Num, Den> {
using type = TYPENAME dimension_pow_impl<downcast_base_t<D>, Num, Den>::type;
};
template<typename... Es, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<derived_dimension_base<Es...>, Num, Den> {
using type = downcast_dimension<derived_dimension_base<exponent_multiply<Es, Num, Den>...>>;
};
} // namespace detail
template<Dimension D, std::intmax_t Num, std::intmax_t Den = 1>
using dimension_pow = TYPENAME detail::dimension_pow_impl<D, Num, Den>::type;
template<Dimension D>
using dimension_sqrt = TYPENAME detail::dimension_pow_impl<D, 1, 2>::type;
} // namespace units
-133
View File
@@ -1,133 +0,0 @@
// 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 <units/bits/basic_concepts.h>
#include <units/reference.h>
#include <units/unit.h>
namespace units {
namespace detail {
template<typename T, typename U>
struct equivalent_impl : std::false_type {};
template<typename T>
struct equivalent_impl<T, T> : std::true_type {};
// units
template<Unit U1, Unit U2>
struct equivalent_impl<U1, U2> : std::disjunction<std::is_base_of<U1, U2>, std::is_base_of<U2, U1>> {};
// dimensions
template<BaseDimension D1, BaseDimension D2>
struct equivalent_impl<D1, D2> :
std::conjunction<std::bool_constant<D1::symbol == D2::symbol>,
same_unit_reference<typename D1::base_unit, typename D2::base_unit>> {};
template<Exponent E1, Exponent E2>
struct equivalent_exp : std::false_type {};
template<BaseDimension Dim1, std::intmax_t Num, std::intmax_t Den, BaseDimension Dim2>
struct equivalent_exp<exponent<Dim1, Num, Den>, exponent<Dim2, Num, Den>> : equivalent_impl<Dim1, Dim2> {};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_derived_dim : std::false_type {};
template<typename... Es1, typename... Es2>
requires(sizeof...(Es1) == sizeof...(Es2))
struct equivalent_derived_dim<derived_dimension_base<Es1...>, derived_dimension_base<Es2...>> :
std::conjunction<equivalent_exp<Es1, Es2>...> {};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_impl<D1, D2> :
std::disjunction<std::is_base_of<D1, D2>, std::is_base_of<D2, D1>,
equivalent_derived_dim<downcast_base_t<D1>, downcast_base_t<D2>>> {};
// additionally accounts for unknown dimensions
template<Unit U1, Dimension D1, Unit U2, Dimension D2>
struct equivalent_unit :
std::disjunction<equivalent_impl<U1, U2>, std::bool_constant<reference<D1, U1>::mag == reference<D2, U2>::mag>> {};
// point origins
template<typename T, typename U>
concept EquivalentPointOrigins =
RebindablePointOriginFor<T, typename U::dimension> && RebindablePointOriginFor<U, typename T::dimension> &&
std::same_as<T, rebind_point_origin_dimension<U, typename T::dimension>> &&
std::same_as<U, rebind_point_origin_dimension<T, typename U::dimension>>;
template<PointOrigin T, PointOrigin U>
struct equivalent_impl<T, U> :
std::bool_constant<EquivalentPointOrigins<T, U> &&
equivalent_impl<typename T::dimension, typename U::dimension>::value> {};
// (point) kinds
template<Kind T, Kind U>
struct equivalent_impl<T, U> :
std::conjunction<std::is_same<typename T::base_kind, typename U::base_kind>,
equivalent_impl<typename T::dimension, typename U::dimension>> {};
template<PointKind T, PointKind U>
struct equivalent_impl<T, U> :
std::conjunction<equivalent_impl<typename T::base_kind, typename U::base_kind>,
equivalent_impl<typename T::origin, typename U::origin>> {};
// quantities, quantity points, quantity (point) kinds
template<Quantity Q1, Quantity Q2>
struct equivalent_impl<Q1, Q2> :
std::conjunction<
equivalent_impl<typename Q1::dimension, typename Q2::dimension>,
equivalent_unit<typename Q1::unit, typename Q1::dimension, typename Q2::unit, typename Q2::dimension>> {};
template<QuantityPoint QP1, QuantityPoint QP2>
struct equivalent_impl<QP1, QP2> :
std::conjunction<equivalent_impl<typename QP1::quantity_type, typename QP2::quantity_type>,
equivalent_impl<typename QP1::origin, typename QP2::origin>> {};
template<QuantityKind QK1, QuantityKind QK2>
struct equivalent_impl<QK1, QK2> :
std::conjunction<equivalent_impl<typename QK1::kind_type, typename QK2::kind_type>,
equivalent_impl<typename QK1::quantity_type, typename QK2::quantity_type>> {};
template<QuantityPointKind QPK1, QuantityPointKind QPK2>
struct equivalent_impl<QPK1, QPK2> :
std::conjunction<equivalent_impl<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>,
equivalent_impl<typename QPK1::origin, typename QPK2::origin>> {};
} // namespace detail
template<typename T, typename U>
inline constexpr bool equivalent = detail::equivalent_impl<T, U>::value;
} // namespace units
-106
View File
@@ -1,106 +0,0 @@
// 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 <units/bits/external/hacks.h>
#include <type_traits>
#ifdef UNITS_DOWNCAST_MODE
#if UNITS_DOWNCAST_MODE < 0 || UNITS_DOWNCAST_MODE > 2
#error "Invalid UNITS_DOWNCAST_MODE value"
#endif
#else
#define UNITS_DOWNCAST_MODE 1
#endif
namespace units {
template<typename BaseType>
struct downcast_base {
using downcast_base_type = BaseType;
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_NON_TEMPLATE_FRIEND
friend auto downcast_guide(downcast_base);
friend auto downcast_poison_pill(downcast_base);
UNITS_DIAGNOSTIC_POP
};
template<typename T>
concept Downcastable =
requires { typename T::downcast_base_type; } && std::derived_from<T, downcast_base<typename T::downcast_base_type>>;
template<typename T>
concept has_downcast_guide = requires(T t) { downcast_guide(t); };
template<typename T>
concept has_downcast_poison_pill = requires(T t) { downcast_poison_pill(t); };
template<typename Target, Downcastable T>
struct downcast_child : T {
friend auto downcast_guide(typename T::downcast_base) { return std::type_identity<Target>(); }
};
template<Downcastable T>
struct downcast_poison : T {
friend auto downcast_poison_pill(typename T::downcast_base) { return true; }
};
enum class downcast_mode {
off = 0, // no downcasting at all
on = 1, // downcasting always forced -> compile-time errors in case of duplicated definitions
automatic = 2 // downcasting automatically enabled if no collisions are present
};
template<typename Target, Downcastable T, downcast_mode mode = static_cast<downcast_mode>(UNITS_DOWNCAST_MODE)>
struct downcast_dispatch :
std::conditional_t<mode == downcast_mode::off, T,
#ifdef UNITS_COMP_MSVC
downcast_child<Target, T>> {
};
#else
std::conditional_t<mode == downcast_mode::automatic && has_downcast_guide<T>, downcast_poison<T>,
downcast_child<Target, T>>> {
};
#endif
namespace detail {
template<typename T>
constexpr auto downcast_impl()
{
if constexpr (has_downcast_guide<downcast_base<T>> && !has_downcast_poison_pill<downcast_base<T>>)
return decltype(downcast_guide(std::declval<downcast_base<T>>()))();
else
return std::type_identity<T>();
}
} // namespace detail
template<Downcastable T>
using downcast = TYPENAME decltype(detail::downcast_impl<T>())::type;
template<Downcastable T>
using downcast_base_t = TYPENAME T::downcast_base_type;
} // namespace units
-143
View File
@@ -1,143 +0,0 @@
// 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 <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
namespace units {
// DimensionOfT
#if UNITS_DOWNCAST_MODE == 0
namespace detail {
template<typename Dim, template<typename...> typename DimTemplate>
inline constexpr bool same_exponents_of = false;
template<Exponent... Es, template<typename...> typename DimTemplate>
inline constexpr bool same_exponents_of<unknown_dimension<Es...>, DimTemplate> =
requires {
typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit<Es...>, typename Es::dimension...>;
} && std::same_as<exponent_list<Es...>, typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit<Es...>,
typename Es::dimension...>::recipe>;
} // namespace detail
template<typename Dim, template<typename...> typename DimTemplate>
concept EquivalentUnknownDimensionOfT = Dimension<Dim> && is_derived_from_specialization_of<Dim, unknown_dimension> &&
detail::same_exponents_of<Dim, DimTemplate>;
#endif
/**
* @brief A concept matching all dimensions matching provided dimension class template
*
* Satisfied by all dimensions being the instantiations derived from the provided dimension
* class template.
*/
template<typename Dim, template<typename...> typename DimTemplate>
concept DimensionOfT = Dimension<Dim> && (is_derived_from_specialization_of<Dim, DimTemplate>
#if UNITS_DOWNCAST_MODE == 0
|| EquivalentUnknownDimensionOfT<Dim, DimTemplate>
#endif
);
/**
* @brief A concept matching all quantities with provided dimension class template
*
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension class template.
*/
template<typename Q, template<typename...> typename DimTemplate>
concept QuantityOfT = Quantity<Q> && DimensionOfT<typename Q::dimension, DimTemplate>;
/**
* @brief A concept matching all quantities with provided dimension
*
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension type.
*/
template<typename Q, typename Dim>
concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dimension, Dim>;
/**
* @brief A concept matching two equivalent quantities
*
* Satisfied by quantities having equivalent dimensions.
*/
template<typename Q1, typename Q2>
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
/**
* @brief A concept matching all quantity points of the provided origin
*
* Satisfied by all quantity points with an origin equivalent to the provided one.
*/
template<typename QP, typename Orig>
concept QuantityPointOf = QuantityPoint<QP> && PointOrigin<Orig> && equivalent<typename QP::origin, Orig>;
/**
* @brief A concept matching two equivalent quantity points
*
* Satisfied by quantity points having equivalent origins.
*/
template<typename QP1, typename QP2>
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::origin>;
/**
* @brief A concept matching only quantity kinds of a specific kind.
*
* @tparam QK Quantity kind to verify.
* @tparam K Kind type to use for verification.
*/
template<typename QK, typename K>
concept QuantityKindOf = QuantityKind<QK> && Kind<K> && equivalent<typename QK::kind_type, K>;
/**
* @brief A concept matching two equivalent quantity kinds
*
* Satisfied by quantity kinds having equivalent kinds.
*/
template<typename QK1, typename QK2>
concept QuantityKindEquivalentTo = QuantityKind<QK1> && QuantityKindOf<QK2, typename QK1::kind_type>;
/**
* @brief A concept matching only quantity point kinds of a specific point kind.
*
* @tparam QPK Quantity point kind to verify.
* @tparam PK Point kind type to use for verification.
*/
template<typename QPK, typename PK>
concept QuantityPointKindOf = QuantityPointKind<QPK> && PointKind<PK> && equivalent<typename QPK::point_kind_type, PK>;
/**
* @brief A concept matching two equivalent quantity point kinds
*
* Satisfied by quantity point kinds having equivalent kinds.
*/
template<typename QPK1, typename QPK2>
concept QuantityPointKindEquivalentTo =
QuantityPointKind<QPK1> && QuantityPointKindOf<QPK2, typename QPK1::point_kind_type>;
} // namespace units
-95
View File
@@ -1,95 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz, Conor Williams, Oliver Schonrock
//
// 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 <gsl/gsl-lite.hpp>
#include <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <type_traits>
namespace units::detail {
template<typename T>
[[nodiscard]] constexpr T abs(T v) noexcept
{
return v < 0 ? -v : v;
}
// Computes the rational gcd of n1/d1 and n2/d2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2) noexcept
{
// Short cut for equal ratios
if (n1 == n2 && d1 == d2) {
return std::array{n1, d1};
}
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2);
assert(std::numeric_limits<std::intmax_t>::max() / n2 > d1);
std::intmax_t num = std::gcd(n1 * d2, n2 * d1);
assert(std::numeric_limits<std::intmax_t>::max() / d1 > d2);
std::intmax_t den = d1 * d2;
std::intmax_t gcd = std::gcd(num, den);
return std::array{num / gcd, den / gcd};
}
constexpr void normalize(std::intmax_t& num, std::intmax_t& den)
{
if (num == 0) {
den = 1;
return;
}
std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd;
}
[[nodiscard]] constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
{
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4);
const std::intmax_t a0 = detail::abs(lhs) % c;
const std::intmax_t a1 = detail::abs(lhs) / c;
const std::intmax_t b0 = detail::abs(rhs) % c;
const std::intmax_t b1 = detail::abs(rhs) / c;
gsl_Expects(a1 == 0 || b1 == 0); // overflow in multiplication
gsl_Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
gsl_Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
gsl_Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
return lhs * rhs;
}
} // namespace units::detail
-166
View File
@@ -1,166 +0,0 @@
// 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 <units/bits/derived_symbol_text.h>
#include <units/bits/external/text_tools.h>
#include <units/derived_dimension.h>
#include <units/prefix.h>
#include <units/unit.h>
namespace units::detail {
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
template<Magnitude auto M>
constexpr auto magnitude_text()
{
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / pow<exp10>(mag<10>());
constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base);
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
constexpr auto num_value = get_value<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den);
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>();
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
auto txt = basic_fixed_string("[") + regular<num_value>();
if constexpr (den_value == 1) {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("]");
} else {
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
}
} else {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
} else {
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
basic_fixed_string("]");
}
}
} else {
return basic_fixed_string("");
}
}
template<Unit U, Magnitude auto M, std::size_t SymbolLen>
constexpr auto prefix_or_magnitude_text()
{
if constexpr (M == mag<1>()) {
// no ratio/prefix
return basic_fixed_string("");
} else {
// try to form a prefix
using prefix = downcast<detail::prefix_base<M>>;
if constexpr (can_be_prefixed<U> && !is_same_v<prefix, prefix_base<M>>) {
// print as a prefixed unit
return prefix::symbol;
} else {
// print as a ratio of the coherent unit
constexpr auto txt = magnitude_text<M>();
if constexpr (SymbolLen > 0 && txt.standard().size() > 0)
return txt + basic_fixed_string(" ");
else
return txt;
}
}
}
template<typename... Es, std::size_t... Idxs>
constexpr auto derived_dimension_unit_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
{
return (exp_text<Es, dimension_unit<typename Es::dimension>::symbol, negative_exp_count<Es...>, Idxs>() + ... +
basic_symbol_text(basic_fixed_string("")));
}
template<typename... Es>
constexpr auto derived_dimension_unit_text(exponent_list<Es...> list)
{
return derived_dimension_unit_text(list, std::index_sequence_for<Es...>());
}
template<Exponent... Es>
constexpr auto exponent_list_with_named_units(exponent_list<Es...>);
template<Exponent Exp>
constexpr auto exponent_list_with_named_units(Exp)
{
using dim = TYPENAME Exp::dimension;
if constexpr (NamedUnit<dimension_unit<dim>>) {
return exponent_list<Exp>();
} else {
using recipe = TYPENAME dim::recipe;
return exponent_list_with_named_units(recipe());
}
}
template<Exponent... Es>
constexpr auto exponent_list_with_named_units(exponent_list<Es...>)
{
return type_list_join<decltype(exponent_list_with_named_units(Es()))...>();
}
constexpr auto exponent_list_with_named_units(exponent_list<> empty) { return empty; }
template<Dimension Dim>
constexpr auto derived_dimension_unit_text()
{
using recipe = TYPENAME Dim::recipe;
return derived_dimension_unit_text(exponent_list_with_named_units(recipe()));
}
template<typename T>
// TODO replace with `inline constexpr bool has_symbol` when MSVC cathes up
concept has_symbol = requires { T::symbol; };
template<Dimension Dim, Unit U>
constexpr auto unit_text()
{
if constexpr (has_symbol<U>) {
// already has a symbol so print it
return U::symbol;
} else {
// print as a prefix or ratio of a coherent unit
using coherent_unit = dimension_unit<Dim>;
constexpr auto symbol_text = []() {
if constexpr (has_symbol<coherent_unit>)
return coherent_unit::symbol;
else
return derived_dimension_unit_text<Dim>();
}();
constexpr auto prefix_txt =
prefix_or_magnitude_text<U, U::mag / coherent_unit::mag, symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
}
} // namespace units::detail
-100
View File
@@ -1,100 +0,0 @@
// 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 <units/customization_points.h>
// IWYU pragma: begin_exports
#include <units/isq/si/time.h>
#include <units/point_origin.h>
#include <chrono>
// IWYU pragma: end_exports
namespace units {
template<typename Rep, typename Period>
struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
private:
static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public:
using dimension = isq::si::dim_time;
using unit = downcast_unit<dimension, mag>;
using rep = Rep;
[[nodiscard]] static constexpr rep number(const std::chrono::duration<Rep, Period>& q) { return q.count(); }
};
template<typename C>
struct clock_origin : point_origin<isq::si::dim_time> {};
template<typename C, typename Rep, typename Period>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
private:
static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public:
using origin = clock_origin<C>;
using unit = downcast_unit<typename origin::dimension, mag>;
using rep = Rep;
[[nodiscard]] static constexpr auto relative(const std::chrono::time_point<C, std::chrono::duration<Rep, Period>>& qp)
{
return qp.time_since_epoch();
}
};
namespace detail {
template<typename C, typename Rep, typename Period>
inline constexpr bool is_quantity_point_like<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> = true;
constexpr std::intmax_t pow_10(std::intmax_t v)
{
gsl_Expects(v > 0);
std::intmax_t res = 1;
for (std::intmax_t i = 0; i < v; i++) res *= 10;
return res;
}
template<ratio R>
constexpr auto to_std_ratio_impl()
{
return std::ratio<R.num, R.den>{};
}
} // namespace detail
// TODO ICE below on gcc-11 when `ratio` is used instead of `auto`
template<auto R>
using to_std_ratio = decltype(detail::to_std_ratio_impl<R>());
template<typename U, typename Rep>
[[nodiscard]] constexpr auto to_std_duration(const quantity<isq::si::dim_time, U, Rep>& q)
{
return std::chrono::duration<Rep, to_std_ratio<as_ratio(U::mag)>>(q.number());
}
template<typename C, typename U, typename Rep>
[[nodiscard]] constexpr auto to_std_time_point(const quantity_point<clock_origin<C>, U, Rep>& qp)
{
using ret_type = std::chrono::time_point<C, std::chrono::duration<Rep, to_std_ratio<as_ratio(U::mag)>>>;
return ret_type(to_std_duration(qp.relative()));
}
} // namespace units
@@ -1,92 +0,0 @@
// 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 <units/base_dimension.h>
#include <units/bits/absolute_magnitude.h>
#include <units/bits/derived_dimension_base.h>
#include <units/bits/dim_consolidate.h>
#include <units/bits/dim_unpack.h>
#include <units/bits/external/downcasting.h>
#include <units/bits/external/type_list.h>
// IWYU pragma: begin_exports
#include <units/exponent.h>
// IWYU pragma: end_exports
namespace units {
namespace detail {
/**
* @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base definition
*
* User provided definition of a derived dimension may contain the same base dimension repeated more than once on the
* list possibly hidden in other derived units provided by the user. The process here should:
* 1. Extract derived dimensions into exponents of base dimensions.
* 2. Sort the exponents so the same dimensions are placed next to each other.
* 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for
* this base dimension.
*/
template<Exponent... Es>
using make_dimension = TYPENAME to_derived_dimension_base<
typename dim_consolidate<type_list_sort<typename dim_unpack<Es...>::type, exponent_less>>::type>::type;
} // namespace detail
/**
* @brief The list of exponents of dimensions (both base and derived) provided by the user
*
* This is the user's interface to create derived dimensions. Exponents list can contain powers of factors of both
* base and derived dimensions. This is called a "recipe" of the dimension and among others is used to print
* unnamed coherent units of this dimension.
*
* Coherent unit is a unit that, for a given system of quantities and for a chosen set of base units, is a product
* of powers of base units with no other proportionality factor than one.
*
* The implementation is responsible for unpacking all of the dimensions into a list containing only base dimensions
* and their factors and putting them to derived_dimension_base class template.
*
* Sometimes units of equivalent quantities in different systems of units do not share the same reference so they
* cannot be easily converted to each other. An example can be a pressure for which a coherent unit in SI is pascal
* and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are
* coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units
* defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of
* such quantities of different systems mag is being used. The result of the division of two
* mag of two quantities of equivalent dimensions in two different systems gives a ratio between their
* coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice
* versa.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam U a coherent unit of a derived dimension
* @tparam Es the list of exponents of ingredient dimensions
*/
template<typename Child, Unit U, Exponent... Es>
struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<Es...>> {
using recipe = exponent_list<Es...>;
using coherent_unit = U;
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag =
detail::absolute_magnitude(typename derived_dimension::exponents()) / U::mag;
};
} // namespace units
-85
View File
@@ -1,85 +0,0 @@
// 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 <units/base_dimension.h>
#include <units/ratio.h>
namespace units {
/**
* @brief A power of factor corresponding to the dimension of a quantity
*
* @tparam Dim component dimension of a derived quantity
* @tparam Num numinator of the factor
* @tparam Den denominator of the factor
*/
template<Dimension Dim, std::intmax_t Num, std::intmax_t Den = 1>
struct exponent {
using dimension = Dim;
static constexpr std::intmax_t num = Num;
static constexpr std::intmax_t den = Den;
};
// is_exponent
namespace detail {
template<typename Dim, std::intmax_t Num, std::intmax_t Den>
inline constexpr bool is_exponent<exponent<Dim, Num, Den>> = true;
} // namespace detail
// exponent_less
template<Exponent E1, Exponent E2>
requires BaseDimension<typename E1::dimension> && BaseDimension<typename E2::dimension>
struct exponent_less : base_dimension_less<typename E1::dimension, typename E2::dimension> {};
// exponent_invert
namespace detail {
template<typename Dim, std::intmax_t Num, std::intmax_t Den>
constexpr exponent<Dim, -Num, Den> exponent_invert_impl(exponent<Dim, Num, Den>);
} // namespace detail
template<Exponent E>
using exponent_invert = decltype(detail::exponent_invert_impl(E()));
// exponent_multiply
namespace detail {
template<Exponent E, std::intmax_t Num, std::intmax_t Den>
struct exponent_multiply_impl {
static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den);
using type = exponent<typename E::dimension, r.num, r.den>;
};
} // namespace detail
template<Exponent E, std::intmax_t Num, std::intmax_t Den>
using exponent_multiply = TYPENAME detail::exponent_multiply_impl<E, Num, Den>::type;
template<Exponent... Es>
struct exponent_list {};
} // namespace units
-128
View File
@@ -1,128 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/quantity.h>
#include <units/reference.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
#include <units/isq/si/prefixes.h>
namespace units {
struct radian : named_unit<radian, "rad"> {};
struct revolution : named_scaled_unit<revolution, "rev", mag<2>() * mag_pi, radian> {};
struct degree : named_scaled_unit<degree, basic_symbol_text{"°", "deg"}, mag<ratio{1, 360}>(), revolution> {};
struct gradian : named_scaled_unit<gradian, basic_symbol_text{"ᵍ", "grad"}, mag<ratio{1, 400}>(), revolution> {};
template<Unit U = radian>
struct dim_angle : base_dimension<"A", U> {};
template<typename T>
concept Angle = QuantityOfT<T, dim_angle>;
template<UnitOf<dim_angle<>> U, Representation Rep = double>
using angle = quantity<dim_angle<>, U, Rep>;
template<Representation Rep = double>
inline constexpr auto cotes_angle = angle<radian, Rep>(1);
#ifndef UNITS_NO_LITERALS
inline namespace literals {
// rad
constexpr auto operator"" _q_rad(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return angle<radian, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_rad(long double l) { return angle<radian, long double>(l); }
// rot
constexpr auto operator"" _q_rev(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return angle<revolution, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_rev(long double l) { return angle<revolution, long double>(l); }
// deg
constexpr auto operator"" _q_deg(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return angle<degree, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_deg(long double l) { return angle<degree, long double>(l); }
// grad
constexpr auto operator"" _q_grad(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return angle<gradian, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_grad(long double l) { return angle<gradian, long double>(l); }
} // namespace literals
#endif // UNITS_NO_LITERALS
#ifndef UNITS_NO_REFERENCES
namespace angle_references {
inline constexpr auto rad = reference<dim_angle<>, radian>{};
inline constexpr auto rev = reference<dim_angle<>, revolution>{};
inline constexpr auto deg = reference<dim_angle<>, degree>{};
inline constexpr auto grad = reference<dim_angle<>, gradian>{};
} // namespace angle_references
namespace references {
using namespace angle_references;
} // namespace references
#endif // UNITS_NO_REFERENCES
} // namespace units
#ifndef UNITS_NO_ALIASES
namespace units::aliases::inline angle {
template<Representation Rep = double>
using rad = units::angle<units::radian, Rep>;
template<Representation Rep = double>
using rev = units::angle<units::revolution, Rep>;
template<Representation Rep = double>
using deg = units::angle<units::degree, Rep>;
template<Representation Rep = double>
using grad = units::angle<units::gradian, Rep>;
} // namespace units::aliases::inline angle
#endif // UNITS_NO_ALIASES
@@ -1,104 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/generic/angle.h>
#include <units/quantity.h>
#include <units/reference.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
#include <units/isq/si/prefixes.h>
namespace units {
struct steradian : named_unit<steradian, "sr"> {};
template<Unit U = steradian>
struct dim_solid_angle : derived_dimension<dim_solid_angle<U>, U, exponent<dim_angle<>, 2>> {};
struct square_degree : derived_scaled_unit<square_degree, dim_solid_angle<>, degree> {};
template<typename T>
concept SolidAngle = QuantityOfT<T, dim_solid_angle>;
template<UnitOf<dim_solid_angle<>> U, Representation Rep = double>
using solid_angle = quantity<dim_solid_angle<>, U, Rep>;
#ifndef UNITS_NO_LITERALS
inline namespace literals {
// sr
constexpr auto operator"" _q_sr(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return solid_angle<steradian, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_sr(long double l) { return solid_angle<steradian, long double>(l); }
// deg2
constexpr auto operator"" _q_deg2(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return solid_angle<square_degree, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_deg2(long double l) { return solid_angle<square_degree, long double>(l); }
} // namespace literals
#endif // UNITS_NO_LITERALS
#ifndef UNITS_NO_REFERENCES
namespace solid_angle_references {
inline constexpr auto sr = reference<dim_solid_angle<>, steradian>{};
inline constexpr auto deg2 = reference<dim_solid_angle<>, square_degree>{};
} // namespace solid_angle_references
namespace references {
using namespace solid_angle_references;
} // namespace references
#endif // UNITS_NO_REFERENCES
} // namespace units
#ifndef UNITS_NO_ALIASES
namespace units::aliases::inline solid_angle {
template<Representation Rep = double>
using sr = units::solid_angle<units::steradian, Rep>;
template<Representation Rep = double>
using deg2 = units::solid_angle<units::square_degree, Rep>;
} // namespace units::aliases::inline solid_angle
#endif // UNITS_NO_ALIASES
-68
View File
@@ -1,68 +0,0 @@
// 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 <units/bits/basic_concepts.h>
#include <units/bits/external/downcasting.h>
namespace units {
template<Dimension D>
struct dynamic_origin;
namespace detail {
template<typename K, Dimension D>
struct _kind_base : downcast_base<_kind_base<K, D>> {
using base_kind = K;
using dimension = D;
};
template<Kind K, PointOrigin O>
struct _point_kind_base : downcast_base<_point_kind_base<K, O>> {
using base_kind = K;
using dimension = typename K::dimension;
using origin = O;
};
} // namespace detail
template<Kind K, Dimension D>
requires Kind<downcast<detail::_kind_base<typename K::base_kind, D>>>
using downcast_kind = downcast<detail::_kind_base<typename K::base_kind, D>>;
template<Kind K, PointOrigin O = dynamic_origin<typename K::dimension>>
requires PointKind<downcast<detail::_point_kind_base<K, O>>>
using downcast_point_kind = downcast<detail::_point_kind_base<K, O>>;
template<typename K, Dimension D>
struct kind : downcast_dispatch<K, detail::_kind_base<K, D>> {};
template<typename DK, Dimension D, Kind BK>
requires std::same_as<BK, typename BK::base_kind>
struct derived_kind : downcast_dispatch<DK, detail::_kind_base<BK, D>> {};
template<typename DPK, Kind BK, PointOrigin O = dynamic_origin<typename BK::dimension>>
struct point_kind : downcast_dispatch<DPK, detail::_point_kind_base<BK, O>> {};
} // namespace units
-397
View File
@@ -1,397 +0,0 @@
// 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 <units/bits/dimension_op.h>
#include <units/bits/external/hacks.h>
#include <units/generic/angle.h>
#include <units/generic/dimensionless.h>
#include <units/quantity.h>
#include <units/unit.h>
// IWYU pragma: begin_exports
#include <cmath>
#include <cstdint>
// IWYU pragma: end_exports
#include <limits>
namespace units {
/**
* @brief Computes the value of a quantity raised to the power `N`
*
* Both the quantity value and its dimension are the base of the operation.
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q>
requires detail::non_zero<Den>
[[nodiscard]] inline auto pow(const Q& q) noexcept
requires requires { pow(q.number(), 1.0); } || requires { std::pow(q.number(), 1.0); }
{
using rep = TYPENAME Q::rep;
if constexpr (Num == 0) {
return rep(1);
} else {
using dim = dimension_pow<typename Q::dimension, Num, Den>;
using unit = downcast_unit<dim, pow<ratio{Num, Den}>(Q::reference.mag) / dim::mag>;
using std::pow;
return quantity<dim, unit, rep>(
static_cast<rep>(pow(q.number(), static_cast<double>(Num) / static_cast<double>(Den))));
}
}
/**
* @brief Computes the square root of a quantity
*
* Both the quantity value and its dimension are the base of the operation.
*
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<Quantity Q>
[[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept
requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); }
{
using dim = dimension_pow<typename Q::dimension, 1, 2>;
using unit = downcast_unit<dim, pow<ratio{1, 2}>(Q::reference.mag) / dim::mag>;
using rep = TYPENAME Q::rep;
using std::sqrt;
return quantity<dim, unit, rep>(static_cast<rep>(sqrt(q.number())));
}
/**
* @brief Computes the cubic root of a quantity
*
* Both the quantity value and its dimension are the base of the operation.
*
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<Quantity Q>
[[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept
requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); }
{
using dim = dimension_pow<typename Q::dimension, 1, 3>;
using unit = downcast_unit<dim, pow<ratio{1, 3}>(Q::reference.mag) / dim::mag>;
using rep = TYPENAME Q::rep;
using std::cbrt;
return quantity<dim, unit, rep>(static_cast<rep>(cbrt(q.number())));
}
/**
* @brief Computes Euler's raised to the given power
*
* @note Such an operation has sense only for a dimensionless quantity.
*
* @param q Quantity being the base of the operation
* @return Quantity The value of the same quantity type
*/
template<typename U, typename Rep>
[[nodiscard]] inline dimensionless<U, Rep> exp(const dimensionless<U, Rep>& q)
requires requires { exp(q.number()); } || requires { std::exp(q.number()); }
{
using std::exp;
return quantity_cast<U>(dimensionless<one, Rep>(exp(quantity_cast<one>(q).number())));
}
/**
* @brief Computes the absolute value of a quantity
*
* @param q Quantity being the base of the operation
* @return Quantity The absolute value of a provided quantity
*/
template<typename D, typename U, typename Rep>
[[nodiscard]] inline quantity<D, U, Rep> abs(const quantity<D, U, Rep>& q) noexcept
requires requires { abs(q.number()); } || requires { std::abs(q.number()); }
{
using std::abs;
return quantity<D, U, Rep>(abs(q.number()));
}
/**
* @brief Returns the epsilon of the quantity
*
* The returned value is defined by a <tt>std::numeric_limits<typename Q::rep>::epsilon()</tt>.
*
* @tparam Q Quantity type being the base of the operation
* @return Quantity The epsilon value for quantity's representation type
*/
template<Quantity Q>
requires requires { std::numeric_limits<typename Q::rep>::epsilon(); }
[[nodiscard]] constexpr Quantity auto epsilon() noexcept
{
return Q(std::numeric_limits<typename Q::rep>::epsilon());
}
/**
* @brief Computes the largest quantity with integer representation and unit type To with its number not greater than q
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To
*/
template<Unit To, typename D, typename U, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> floor(const quantity<D, U, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { floor(q.number()); } ||
requires { std::floor(q.number()); }) &&
(std::same_as<To, U> || requires {
::units::quantity_cast<To>(q);
quantity<D, To, Rep>::one();
})
{
const auto handle_signed_results = [&]<typename T>(const T& res) {
if (res > q) {
return res - T::one();
}
return res;
};
if constexpr (treat_as_floating_point<Rep>) {
using std::floor;
if constexpr (std::is_same_v<To, U>) {
return quantity<D, To, Rep>(floor(q.number()));
} else {
return handle_signed_results(quantity<D, To, Rep>(floor(quantity_cast<To>(q).number())));
}
} else {
if constexpr (std::is_same_v<To, U>) {
return q;
} else {
return handle_signed_results(quantity_cast<To>(q));
}
}
}
/**
* @brief Overload of @c ::units::floor<Unit>() using the unit type of To
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type of quantity To
*/
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> floor(const quantity<D, U, Rep>& q) noexcept
requires requires { ::units::floor<typename To::unit>(q); }
{
return ::units::floor<typename To::unit>(q);
}
/**
* @brief Computes the smallest quantity with integer representation and unit type To with its number not less than q
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To
*/
template<Unit To, typename D, typename U, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> ceil(const quantity<D, U, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) &&
(std::same_as<To, U> || requires {
::units::quantity_cast<To>(q);
quantity<D, To, Rep>::one();
})
{
const auto handle_signed_results = [&]<typename T>(const T& res) {
if (res < q) {
return res + T::one();
}
return res;
};
if constexpr (treat_as_floating_point<Rep>) {
using std::ceil;
if constexpr (std::is_same_v<To, U>) {
return quantity<D, To, Rep>(ceil(q.number()));
} else {
return handle_signed_results(quantity<D, To, Rep>(ceil(quantity_cast<To>(q).number())));
}
} else {
if constexpr (std::is_same_v<To, U>) {
return q;
} else {
return handle_signed_results(quantity_cast<To>(q));
}
}
}
/**
* @brief Overload of @c ::units::ceil<Unit>() using the unit type of To
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type of quantity To
*/
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> ceil(const quantity<D, U, Rep>& q) noexcept
requires requires { ::units::ceil<typename To::unit>(q); }
{
return ::units::ceil<typename To::unit>(q);
}
/**
* @brief Computes the nearest quantity with integer representation and unit type To to q
*
* Rounding halfway cases away from zero, regardless of the current rounding mode.
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To
*/
template<Unit To, typename D, typename U, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> round(const quantity<D, U, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { round(q.number()); } ||
requires { std::round(q.number()); }) &&
(std::same_as<To, U> || requires {
::units::floor<To>(q);
quantity<D, To, Rep>::one();
})
{
if constexpr (std::is_same_v<To, U>) {
if constexpr (treat_as_floating_point<Rep>) {
using std::round;
return quantity<D, To, Rep>(round(q.number()));
} else {
return q;
}
} else {
const auto res_low = units::floor<To>(q);
const auto res_high = res_low + decltype(res_low)::one();
const auto diff0 = q - res_low;
const auto diff1 = res_high - q;
if (diff0 == diff1) {
if (static_cast<int>(res_low.number()) & 1) {
return res_high;
}
return res_low;
}
if (diff0 < diff1) {
return res_low;
}
return res_high;
}
}
/**
* @brief Overload of @c ::units::round<Unit>() using the unit type of To
*
* @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type of quantity To
*/
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> round(const quantity<D, U, Rep>& q) noexcept
requires requires { ::units::round<typename To::unit>(q); }
{
return ::units::round<typename To::unit>(q);
}
/**
* @brief Computes the square root of the sum of the squares of x and y,
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2>
[[nodiscard]] inline std::common_type_t<Q1, Q2> hypot(const Q1& x, const Q2& y) noexcept
requires requires { typename std::common_type_t<Q1, Q2>; } && requires(std::common_type_t<Q1, Q2> q) {
requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); };
}
{
using type = std::common_type_t<Q1, Q2>;
type xx = x;
type yy = y;
using std::hypot;
return type(hypot(xx.number(), yy.number()));
}
/**
* @brief Computes the square root of the sum of the squares of x, y, and z,
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2, Quantity Q3>
[[nodiscard]] inline std::common_type_t<Q1, Q2, Q3> hypot(const Q1& x, const Q2& y, const Q3& z) noexcept
requires requires { typename std::common_type_t<Q1, Q2, Q3>; } && requires(std::common_type_t<Q1, Q2, Q3> q) {
requires requires { hypot(q.number(), q.number(), q.number()); } ||
requires { std::hypot(q.number(), q.number(), q.number()); };
}
{
using type = std::common_type_t<Q1, Q2, Q3>;
type xx = x;
type yy = y;
type zz = z;
using std::hypot;
return type(hypot(xx.number(), yy.number(), zz.number()));
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline dimensionless<one, Rep> sin(const angle<U, Rep>& q) noexcept
requires requires { sin(q.number()); } || requires { std::sin(q.number()); }
{
using std::sin;
return sin(quantity_cast<radian>(q).number());
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline dimensionless<one, Rep> cos(const angle<U, Rep>& q) noexcept
requires requires { cos(q.number()); } || requires { std::cos(q.number()); }
{
using std::cos;
return cos(quantity_cast<radian>(q).number());
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline dimensionless<one, Rep> tan(const angle<U, Rep>& q) noexcept
requires requires { tan(q.number()); } || requires { std::tan(q.number()); }
{
using std::tan;
return tan(quantity_cast<radian>(q).number());
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline angle<radian, Rep> asin(const dimensionless<U, Rep>& q) noexcept
requires requires { asin(q.number()); } || requires { std::asin(q.number()); }
{
using std::asin;
return angle<radian, Rep>(asin(quantity_cast<one>(q).number()));
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline angle<radian, Rep> acos(const dimensionless<U, Rep>& q) noexcept
requires requires { acos(q.number()); } || requires { std::acos(q.number()); }
{
using std::acos;
return angle<radian, Rep>(acos(quantity_cast<one>(q).number()));
}
template<typename U, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline angle<radian, Rep> atan(const dimensionless<U, Rep>& q) noexcept
requires requires { atan(q.number()); } || requires { std::atan(q.number()); }
{
using std::atan;
return angle<radian, Rep>(atan(quantity_cast<one>(q).number()));
}
} // namespace units
-62
View File
@@ -1,62 +0,0 @@
// 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 <units/bits/basic_concepts.h>
#include <units/bits/external/downcasting.h>
// IWYU pragma: begin_exports
#include <units/magnitude.h>
#include <units/ratio.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
namespace units {
namespace detail {
template<Magnitude auto M>
struct prefix_base : downcast_base<prefix_base<M>> {
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M;
};
} // namespace detail
/**
* @brief A prefix used to scale units
*
* Data from a prefix class is used in two cases:
* - when defining a prefixed_unit its ratio is used to scale the reference unit and its
* symbol is used to prepend to the symbol of referenced unit
* - when printing the symbol of a scaled unit that was not predefined by the user but its
* factor matches ratio of a prefix, its symbol will be prepended to the symbol of the unit
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a text representation of the prefix
* @tparam R factor to be used to scale a unit
*/
template<typename Child, basic_symbol_text Symbol, Magnitude auto M>
struct prefix : downcast_dispatch<Child, detail::prefix_base<M>, downcast_mode::on> {
static constexpr auto symbol = Symbol;
};
} // namespace units
-535
View File
@@ -1,535 +0,0 @@
// 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 <units/bits/common_type.h>
#include <units/generic/dimensionless.h>
// IWYU pragma: begin_exports
#include <units/quantity_cast.h>
#include <units/ratio.h>
#include <compare>
// IWYU pragma: end_exports
#include <units/reference.h>
#include <utility>
namespace units {
namespace detail {
template<Reference auto R> // TODO: Replace with `v * R` pending https://github.com/BobSteagall/wg21/issues/58.
inline constexpr auto make_quantity = [](auto&& v) {
using Rep = std::remove_cvref_t<decltype(v)>;
using ref_type = decltype(R);
using dimension = TYPENAME ref_type::dimension;
using unit = TYPENAME ref_type::unit;
return quantity<dimension, unit, Rep>(std::forward<decltype(v)>(v));
};
template<typename T>
concept quantity_one =
Quantity<T> &&
(std::same_as<typename T::dimension, dim_one> || std::same_as<typename T::dimension, unknown_dimension<>>)&&detail::
equivalent_unit<typename T::unit, typename T::dimension, ::units::one, typename T::dimension>::value;
} // namespace detail
template<typename T>
concept floating_point_ = // exposition only
(Quantity<T> && treat_as_floating_point<typename T::rep>) || (!Quantity<T> && treat_as_floating_point<T>);
template<typename From, typename To>
concept safe_convertible_to_ = // exposition only
(!Quantity<From>)&&(!Quantity<To>)&&std::convertible_to<From, To> &&
(floating_point_<To> || (!floating_point_<From>));
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
concept harmonic_ = // exposition only
Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>);
template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only
Quantity<QFrom> && QuantityOf<QTo, typename QFrom::dimension> &&
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U>
concept quantity_value_for_ = std::regular_invocable<Func, T, U> && Representation<std::invoke_result_t<Func, T, U>>;
template<typename T, typename Func, typename U, typename V>
concept invoke_result_convertible_to_ =
Representation<T> && quantity_value_for_<Func, U, V> && safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
template<typename Func, typename Q, typename V>
concept have_quantity_for_ = Quantity<Q> && (!Quantity<V>)&&quantity_value_for_<Func, typename Q::rep, V>;
template<QuantityLike Q>
using quantity_like_type = quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit,
typename quantity_like_traits<Q>::rep>;
/**
* @brief A quantity
*
* Property of a phenomenon, body, or substance, where the property has a magnitude that can be
* expressed by means of a number and a measurement unit.
*
* @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension)
* @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Dimension D, UnitOf<D> U, Representation Rep = double>
class quantity {
Rep number_;
public:
// member types and values
using dimension = D;
using unit = U;
using rep = Rep;
static constexpr units::reference<dimension, unit> reference{};
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return quantity(quantity_values<rep>::zero());
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return quantity(quantity_values<rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return quantity(quantity_values<rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return quantity(quantity_values<rep>::max());
}
// construction, assignment, destruction
quantity() = default;
quantity(const quantity&) = default;
quantity(quantity&&) = default;
template<typename Value>
requires(safe_convertible_to_<std::remove_cvref_t<Value>, rep>)
constexpr explicit(!detail::quantity_one<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
{
}
template<safe_castable_to_<quantity> Q>
constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast<quantity>(q).number())
{
}
template<QuantityLike Q>
requires(safe_castable_to_<quantity_like_type<Q>, quantity>)
constexpr explicit quantity(const Q& q) : quantity(quantity_like_type<Q>(quantity_like_traits<Q>::number(q)))
{
}
quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;
// data access
[[nodiscard]] constexpr rep& number() & noexcept { return number_; }
[[nodiscard]] constexpr const rep& number() const& noexcept { return number_; }
[[nodiscard]] constexpr rep&& number() && noexcept { return std::move(number_); }
[[nodiscard]] constexpr const rep&& number() const&& noexcept { return std::move(number_); }
// member unary operators
[[nodiscard]] constexpr Quantity auto operator+() const
requires requires(rep v) {
{
+v
} -> std::common_with<rep>;
}
{
using ret = quantity<D, U, decltype(+number())>;
return ret(+number());
}
[[nodiscard]] constexpr Quantity auto operator-() const
requires(std::regular_invocable<std::negate<>, rep>)
{
using ret = quantity<D, U, decltype(-number())>;
return ret(-number());
}
constexpr quantity& operator++()
requires requires(rep v) {
{
++v
} -> std::same_as<rep&>;
}
{
++number_;
return *this;
}
[[nodiscard]] constexpr quantity operator++(int)
requires requires(rep v) {
{
v++
} -> std::same_as<rep>;
}
{
return quantity(number_++);
}
constexpr quantity& operator--()
requires requires(rep v) {
{
--v
} -> std::same_as<rep&>;
}
{
--number_;
return *this;
}
[[nodiscard]] constexpr quantity operator--(int)
requires requires(rep v) {
{
v--
} -> std::same_as<rep>;
}
{
return quantity(number_--);
}
constexpr quantity& operator+=(const quantity& q)
requires requires(rep a, rep b) {
{
a += b
} -> std::same_as<rep&>;
}
{
number_ += q.number();
return *this;
}
constexpr quantity& operator-=(const quantity& q)
requires requires(rep a, rep b) {
{
a -= b
} -> std::same_as<rep&>;
}
{
number_ -= q.number();
return *this;
}
template<typename Rep2>
constexpr quantity& operator*=(const Rep2& rhs)
requires requires(rep a, const Rep2 b) {
{
a *= b
} -> std::same_as<rep&>;
}
{
number_ *= rhs;
return *this;
}
template<detail::quantity_one Q>
constexpr quantity& operator*=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
a *= b
} -> std::same_as<rep&>;
}
{
number_ *= rhs.number();
return *this;
}
template<typename Rep2>
constexpr quantity& operator/=(const Rep2& rhs)
requires requires(rep a, const Rep2 b) {
{
a /= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
number_ /= rhs;
return *this;
}
template<detail::quantity_one Q>
constexpr quantity& operator/=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
a /= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
number_ /= rhs.number();
return *this;
}
template<typename Rep2>
constexpr quantity& operator%=(const Rep2& rhs)
requires(!floating_point_<rep>) && (!floating_point_<Rep2>) && requires(rep a, const Rep2 b) {
{
a %= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
number_ %= rhs;
return *this;
}
template<detail::quantity_one Q>
constexpr quantity& operator%=(const Q& rhs)
requires(!floating_point_<rep>) && (!floating_point_<typename Q::rep>) && requires(rep a, const typename Q::rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
number_ %= rhs.number();
return *this;
}
constexpr quantity& operator%=(const quantity& q)
requires(!floating_point_<rep>) && requires(rep a, rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(q.number() != quantity_values<rep>::zero());
number_ %= q.number();
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Value& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires invoke_result_convertible_to_<rep, std::plus<>, rep, Value>;
}
{
return units::quantity(lhs.number() + rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires invoke_result_convertible_to_<rep, std::plus<>, Value, rep>;
}
{
return units::quantity(lhs + rhs.number());
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Value& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires invoke_result_convertible_to_<rep, std::minus<>, rep, Value>;
}
{
return units::quantity(lhs.number() - rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires invoke_result_convertible_to_<rep, std::minus<>, Value, rep>;
}
{
return units::quantity(lhs - rhs.number());
}
template<Representation Value>
requires(invoke_result_convertible_to_<rep, std::multiplies<>, rep, const Value&>)
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, rep, Value>>;
return ret(q.number() * v);
}
template<Representation Value>
requires(invoke_result_convertible_to_<rep, std::multiplies<>, const Value&, rep>)
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, Value, rep>>;
return ret(v * q.number());
}
template<Representation Value>
requires(invoke_result_convertible_to_<rep, std::divides<>, rep, const Value&>)
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::divides<>, rep, Value>>;
return ret(q.number() / v);
}
template<Representation Value>
requires(invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>)
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
return detail::make_quantity<::units::reference<dim_one, ::units::one>{} / reference>(v / q.number());
}
template<typename Value>
requires(!Quantity<Value>) && (!floating_point_<rep>) && (!floating_point_<Value>) &&
(invoke_result_convertible_to_<rep, std::modulus<>, rep, const Value&>)
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, Value>>;
return ret(q.number() % v);
}
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs)
requires(!floating_point_<rep>) && (invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<rep>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, rep>>;
return ret(lhs.number() % rhs.number());
}
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs)
requires std::three_way_comparable<rep>
#if UNITS_COMP_GCC == 10 && UNITS_COMP_GCC_MINOR >= 2
= default;
#else
{
return lhs.number() <=> rhs.number();
}
#endif
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) = default;
};
// CTAD
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 17
template<typename D, typename U, typename Rep>
explicit(false) quantity(Rep&&) -> quantity<D, U, Rep>;
#endif
template<typename D, typename U, typename Rep>
explicit(false) quantity(quantity<D, U, Rep>) -> quantity<D, U, Rep>;
template<Representation Rep>
explicit(false) quantity(Rep) -> quantity<dim_one, one, Rep>;
template<QuantityLike Q>
explicit quantity(Q) -> quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit,
typename quantity_like_traits<Q>::rep>;
// non-member binary operators
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires(quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ref = detail::common_quantity_reference<Q1, Q2>;
using ret = quantity<typename ref::dimension, typename ref::unit, decltype(lhs.number() + rhs.number())>;
return ret(ret(lhs).number() + ret(rhs).number());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires(quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ref = detail::common_quantity_reference<Q1, Q2>;
using ret = quantity<typename ref::dimension, typename ref::unit, decltype(lhs.number() - rhs.number())>;
return ret(ret(lhs).number() - ret(rhs).number());
}
template<Quantity Q1, Quantity Q2>
requires(quantity_value_for_<std::multiplies<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs)
{
return detail::make_quantity<Q1::reference * Q2::reference>(lhs.number() * rhs.number());
}
template<Quantity Q1, Quantity Q2>
requires(quantity_value_for_<std::divides<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());
return detail::make_quantity<Q1::reference / Q2::reference>(lhs.number() / rhs.number());
}
template<Quantity Q1, Quantity Q2>
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
(QuantityEquivalentTo<Q2, Q1> || Dimensionless<Q2>) &&
(quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());
using ret = quantity<typename Q1::dimension, typename Q1::unit,
std::invoke_result_t<std::modulus<>, typename Q1::rep, typename Q2::rep>>;
return ret(lhs.number() % rhs.number());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
using cq = std::common_type_t<Q1, Q2>;
return cq(lhs).number() <=> cq(rhs).number();
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
using cq = std::common_type_t<Q1, Q2>;
return cq(lhs).number() == cq(rhs).number();
}
// type traits
namespace detail {
template<typename D, typename U, typename Rep>
inline constexpr bool is_quantity<quantity<D, U, Rep>> = true;
template<typename T>
requires units::is_derived_from_specialization_of<T, units::quantity>
inline constexpr bool is_quantity<T> = true;
} // namespace detail
} // namespace units
-382
View File
@@ -1,382 +0,0 @@
// 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 <units/bits/dimension_op.h>
#include <units/bits/external/type_traits.h>
#include <units/concepts.h>
#include <units/customization_points.h>
#include <units/magnitude.h>
#include <units/reference.h>
UNITS_DIAGNOSTIC_PUSH
// warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int
UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA
namespace units {
template<Dimension D, UnitOf<D> U, Representation Rep>
class quantity;
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
class quantity_point;
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
class quantity_kind;
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
class quantity_point_kind;
namespace detail {
template<Quantity Q>
inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
template<typename QFrom, typename QTo>
inline constexpr Magnitude auto cast_magnitude = [] {
using FromU = TYPENAME QFrom::unit;
using ToU = TYPENAME QTo::unit;
if constexpr (same_unit_reference<FromU, ToU>::value) {
return FromU::mag / ToU::mag;
} else {
return quantity_magnitude<QFrom> / quantity_magnitude<QTo>;
}
}();
template<typename From, typename To>
struct cast_traits;
template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type;
};
template<typename From, typename To>
requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
requires { typename std::common_type_t<From, To>::value_type; } &&
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>)
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
using rep_type = std::common_type_t<From, To>;
};
template<typename From, typename To>
requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
requires { typename std::common_type_t<From, To>::element_type; } &&
common_type_with_<typename std::common_type_t<From, To>::element_type, std::intmax_t>)
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::element_type, std::intmax_t>;
using rep_type = std::common_type_t<From, To>;
};
} // namespace detail
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets the target quantity type to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::time<units::isq::si::second>>(1_q_ms);
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
requires QuantityOf<To, D> && (std::constructible_from<typename To::rep, std::common_type_t<typename To::rep, Rep>>)
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
using traits = detail::cast_traits<Rep, typename To::rep>;
using ratio_type = TYPENAME traits::ratio_type;
using rep_type = TYPENAME traits::rep_type;
constexpr Magnitude auto c_mag = detail::cast_magnitude<quantity<D, U, Rep>, To>;
constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto irr = c_mag * (den / num);
constexpr auto val = [](Magnitude auto m) { return get_value<ratio_type>(m); };
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only the target dimension to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::dim_acceleration>(200_q_Gal);
*
* @tparam ToD a dimension type to use for a target quantity
*/
template<Dimension ToD, typename D, typename U, typename Rep>
requires equivalent<ToD, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<ToD, downcast_unit<ToD, U::mag>, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only the target unit to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::second>(1_q_ms);
*
* @tparam ToU a unit type to use for a target quantity
*/
template<Unit ToU, typename D, typename U, typename Rep>
requires UnitOf<ToU, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<D, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets both the target dimension and unit to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
*
* @note This cast is especially useful when working with quantities of unknown dimensions
* (@c unknown_dimension).
*
* @tparam ToD a dimension type to use for a target quantity
* @tparam ToU a unit type to use for a target quantity
*/
template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
requires equivalent<ToD, D> && UnitOf<ToU, ToD>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<ToD, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only representation to cast to. For example:
*
* auto q1 = units::quantity_cast<int>(1_q_ms);
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<Representation ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<D, U, ToRep>>(q);
}
/**
* @brief Explicit cast of a quantity point
*
* Implicit conversions between quantity points of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example:
*
* auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<units::isq::si::time<units::isq::si::second>>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<units::isq::si::dim_acceleration>(quantity_point{200_q_Gal});
* auto q1 = units::quantity_point_cast<units::isq::si::second>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
*
* @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast
*/
template<typename CastSpec, typename O, typename U, typename Rep>
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp)
requires requires {
requires is_specialization_of<CastSpec, quantity_point>;
requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); };
requires equivalent<O, typename CastSpec::origin>;
} || // TODO: Simplify when Clang catches up.
requires { quantity_cast<CastSpec>(qp.relative()); }
{
if constexpr (is_specialization_of<CastSpec, quantity_point>)
return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
else
return quantity_point(quantity_cast<CastSpec>(qp.relative()));
}
/**
* @brief Explicit cast of a quantity point
*
* Implicit conversions between quantity points of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target dimension and unit to cast to. For example:
*
* auto q1 = units::quantity_point_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
*
* @note This cast is especially useful when working with quantity points of unknown dimensions
* (@c unknown_dimension).
*
* @tparam ToD a dimension type to use for a target quantity
* @tparam ToU a unit type to use for a target quantity
*/
template<Dimension ToD, Unit ToU, typename O, typename U, typename Rep>
requires equivalent<ToD, typename O::dimension> && UnitOf<ToU, ToD> && RebindablePointOriginFor<O, ToD>
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& q)
{
return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity kind
*
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example:
*
* auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
* auto q1 = units::quantity_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<units::isq::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
* auto q1 = units::quantity_kind_cast<units::isq::si::metre>(ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
*
* @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
*/
template<typename CastSpec, typename K, typename U, typename Rep>
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
requires requires {
requires is_specialization_of<CastSpec, quantity_kind>;
requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); };
} || requires {
requires Kind<CastSpec>;
requires UnitOf<U, typename CastSpec::dimension>;
} || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
{
if constexpr (is_specialization_of<CastSpec, quantity_kind>)
return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
else if constexpr (Kind<CastSpec>)
return quantity_kind<CastSpec, U, Rep>(qk.common());
else {
auto q{quantity_cast<CastSpec>(qk.common())};
using Q = decltype(q);
return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
}
}
/**
* @brief Explicit cast of a quantity kind
*
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target kind and unit to cast to. For example:
*
* auto q1 = units::quantity_kind_cast<ns::height_kind, units::isq::si::kilometre>(w);
*
* @note This cast is especially useful when working with quantity kinds of unknown kind.
*
* @tparam ToK the kind type to use for the target quantity
* @tparam ToU the unit type to use for the target quantity
*/
template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
{
return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
}
/**
* @brief Explicit cast of a quantity point kind
*
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For
* example:
*
* auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<decltype(ns::width{1 * m})>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
* auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
* auto q1 = units::quantity_point_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200
* * Gal})); auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm}); auto q1 =
* units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
*
* @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
*/
template<typename CastSpec, typename PK, typename U, typename Rep>
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
requires requires {
requires is_specialization_of<CastSpec, quantity_point_kind>;
requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); };
requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>;
} || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
{
if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
else if constexpr (PointKind<CastSpec>)
return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
else
return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
}
/**
* @brief Explicit cast of a quantity point kind
*
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target point kind and unit to cast to. For example:
*
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::isq::si::kilometre>(x);
*
* @note This cast is especially useful when working with quantity point kinds of unknown point kind.
*
* @tparam ToPK the point kind type to use for the target quantity
* @tparam ToU the unit type to use for the target quantity
*/
template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
{
return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
}
} // namespace units
UNITS_DIAGNOSTIC_POP
-409
View File
@@ -1,409 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/kind.h>
#include <units/quantity.h>
// IWYU pragma: end_exports
#include <concepts>
#include <type_traits>
#include <utility>
namespace units {
namespace detail {
template<Kind K>
inline constexpr auto make_quantity_kind_fn = [](auto&& q) {
using Q = std::remove_reference_t<decltype(q)>;
return quantity_kind<K, typename Q::unit, typename Q::rep>(std::forward<decltype(q)>(q));
};
template<QuantityKind QK>
inline constexpr auto& make_quantity_kind = make_quantity_kind_fn<typename QK::kind_type>;
template<Kind K>
inline constexpr auto downcasted_kind_fn = [](auto q) {
using Q = decltype(q);
return make_quantity_kind_fn<downcast_kind<K, typename Q::dimension>>(std::move(q));
};
template<QuantityKind QK>
inline constexpr auto& downcasted_kind = downcasted_kind_fn<typename QK::kind_type::base_kind>;
template<typename T, typename K>
concept quantity_kind_one = QuantityKind<T> && equivalent<typename T::kind_type, downcast_kind<K, dim_one>> &&
detail::quantity_one<typename T::quantity_type>;
} // namespace detail
/**
* @brief A concept matching two related quantity kinds
*
* Satisfied by quantity kinds having equivalent base kinds.
*/
template<typename QK1, typename QK2>
concept QuantityKindRelatedTo = QuantityKind<QK1> && QuantityKind<QK2> &&
equivalent<typename QK1::kind_type::base_kind, typename QK2::kind_type::base_kind>;
/**
* @brief A quantity kind
*
* A quantity with more specific usage as determined by its kind.
* See https://jcgm.bipm.org/vim/en/1.2.html and NOTE 1 at https://jcgm.bipm.org/vim/en/1.1.html.
*
* @tparam K the kind of quantity
* @tparam U the measurement unit of the quantity kind
* @tparam Rep the type to be used to represent values of the quantity kind
*/
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep = double>
class quantity_kind {
public:
using kind_type = K;
using quantity_type = quantity<typename K::dimension, U, Rep>;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
static constexpr units::reference<dimension, unit> reference{};
private:
quantity_type q_;
public:
quantity_kind() = default;
quantity_kind(const quantity_kind&) = default;
quantity_kind(quantity_kind&&) = default;
template<typename T>
requires(Quantity<std::remove_cvref_t<T>> || QuantityLike<std::remove_cvref_t<T>> ||
(Dimensionless<quantity_type> && !Quantity<std::remove_cvref_t<T>>)) &&
std::constructible_from<quantity_type, T>
constexpr explicit quantity_kind(T&& t) : q_(std::forward<T>(t))
{
}
template<QuantityKindEquivalentTo<quantity_kind> QK2>
requires std::convertible_to<typename QK2::quantity_type, quantity_type>
constexpr explicit(false) quantity_kind(const QK2& qk) : q_(qk.common())
{
}
quantity_kind& operator=(const quantity_kind&) = default;
quantity_kind& operator=(quantity_kind&&) = default;
[[nodiscard]] constexpr quantity_type& common() & noexcept { return q_; }
[[nodiscard]] constexpr const quantity_type& common() const& noexcept { return q_; }
[[nodiscard]] constexpr quantity_type&& common() && noexcept { return std::move(q_); }
[[nodiscard]] constexpr const quantity_type&& common() const&& noexcept { return std::move(q_); }
[[nodiscard]] static constexpr quantity_kind zero() noexcept
requires requires { quantity_type::zero(); }
{
return quantity_kind(quantity_type::zero());
}
[[nodiscard]] static constexpr quantity_kind one() noexcept
requires requires { quantity_type::one(); }
{
return quantity_kind(quantity_type::one());
}
[[nodiscard]] static constexpr quantity_kind min() noexcept
requires requires { quantity_type::min(); }
{
return quantity_kind(quantity_type::min());
}
[[nodiscard]] static constexpr quantity_kind max() noexcept
requires requires { quantity_type::max(); }
{
return quantity_kind(quantity_type::max());
}
[[nodiscard]] constexpr quantity_kind operator+() const
requires requires(quantity_type q) { +q; }
{
return *this;
}
[[nodiscard]] constexpr QuantityKind auto operator-() const
requires requires(quantity_type q) { -q; }
{
return detail::make_quantity_kind<quantity_kind>(-q_);
}
constexpr quantity_kind& operator++()
requires requires(quantity_type q) { ++q; }
{
++q_;
return *this;
}
[[nodiscard]] constexpr quantity_kind operator++(int)
requires requires(quantity_type q) { q++; }
{
return quantity_kind(q_++);
}
constexpr quantity_kind& operator--()
requires requires(quantity_type q) { --q; }
{
--q_;
return *this;
}
[[nodiscard]] constexpr quantity_kind operator--(int)
requires requires(quantity_type q) { q--; }
{
return quantity_kind(q_--);
}
constexpr quantity_kind& operator+=(const quantity_kind& qk)
requires requires(quantity_type q) { q += qk.common(); }
{
q_ += qk.common();
return *this;
}
constexpr quantity_kind& operator-=(const quantity_kind& qk)
requires requires(quantity_type q) { q -= qk.common(); }
{
q_ -= qk.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator*=(const Rep2& rhs)
requires requires(quantity_type q) { q *= rhs; }
{
q_ *= rhs;
return *this;
}
template<detail::quantity_kind_one<K> QK>
constexpr quantity_kind& operator*=(const QK& rhs)
requires requires(quantity_type q) { q *= rhs.common(); }
{
q_ *= rhs.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator/=(const Rep2& rhs)
requires requires(quantity_type q) { q /= rhs; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
q_ /= rhs;
return *this;
}
template<detail::quantity_kind_one<K> QK>
constexpr quantity_kind& operator/=(const QK& rhs)
requires requires(quantity_type q) { q /= rhs.common(); }
{
q_ /= rhs.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator%=(const Rep2& rhs)
requires(!Quantity<Rep2> || Dimensionless<Rep2>) && requires(quantity_type q, const Rep2 r) { q %= r; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
q_ %= rhs;
return *this;
}
template<QuantityKind QK>
constexpr quantity_kind& operator%=(const QK& rhs)
requires(QuantityKindEquivalentTo<QK, quantity_kind> || detail::quantity_kind_one<QK, K>) &&
requires(quantity_type q) { q %= rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().number() != quantity_values<typename QK::rep>::zero());
q_ %= rhs.common();
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<Representation Value>
[[nodiscard]] friend constexpr QuantityKind auto operator*(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) {
{
q* v
} -> Quantity;
}
{
return detail::make_quantity_kind<quantity_kind>(qk.common() * v);
}
template<Representation Value>
[[nodiscard]] friend constexpr QuantityKind auto operator*(const Value& v, const quantity_kind& qk)
requires requires(quantity_type q) {
{
v* q
} -> Quantity;
}
{
return detail::make_quantity_kind<quantity_kind>(v * qk.common());
}
template<Representation Value>
[[nodiscard]] friend constexpr QuantityKind auto operator/(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) {
{
q / v
} -> Quantity;
}
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
return detail::make_quantity_kind<quantity_kind>(qk.common() / v);
}
template<Representation Value>
[[nodiscard]] friend constexpr QuantityKind auto operator/(const Value& v, const quantity_kind& qk)
requires requires(quantity_type q) {
{
v / q
} -> Quantity;
}
{
gsl_ExpectsAudit(qk.common().number() != quantity_values<rep>::zero());
return detail::downcasted_kind<quantity_kind>(v / qk.common());
}
template<Representation Value>
[[nodiscard]] friend constexpr QuantityKind auto operator%(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) { q % v; }
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
return detail::make_quantity_kind<quantity_kind>(qk.common() % v);
}
[[nodiscard]] friend constexpr auto operator<=>(const quantity_kind& lhs, const quantity_kind& rhs)
requires std::three_way_comparable<quantity_type>
{
return lhs.common() <=> rhs.common();
}
[[nodiscard]] friend constexpr bool operator==(const quantity_kind&, const quantity_kind&) = default;
};
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator+(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() + rhs.common(); }
{
return detail::make_quantity_kind<QK1>(lhs.common() + rhs.common());
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator-(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() - rhs.common(); }
{
return detail::make_quantity_kind<QK1>(lhs.common() - rhs.common());
}
template<QuantityKind QK, Quantity Q>
[[nodiscard]] constexpr QuantityKind auto operator*(const QK& lhs, const Q& rhs)
requires requires { lhs.common() * rhs; }
{
return detail::downcasted_kind<QK>(lhs.common() * rhs);
}
template<Quantity Q, QuantityKind QK>
[[nodiscard]] constexpr QuantityKind auto operator*(const Q& lhs, const QK& rhs)
requires requires { lhs* rhs.common(); }
{
return detail::downcasted_kind<QK>(lhs * rhs.common());
}
template<QuantityKind QK1, QuantityKindRelatedTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator*(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() * rhs.common(); }
{
return detail::downcasted_kind<QK1>(lhs.common() * rhs.common());
}
template<QuantityKind QK, Quantity Q>
[[nodiscard]] constexpr QuantityKind auto operator/(const QK& lhs, const Q& rhs)
requires requires { lhs.common() / rhs; }
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
return detail::downcasted_kind<QK>(lhs.common() / rhs);
}
template<Quantity Q, QuantityKind QK>
[[nodiscard]] constexpr QuantityKind auto operator/(const Q& lhs, const QK& rhs)
requires requires { lhs / rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().number() != quantity_values<typename QK::rep>::zero());
return detail::downcasted_kind<QK>(lhs / rhs.common());
}
template<QuantityKind QK1, QuantityKindRelatedTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator/(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() / rhs.common(); }
{
return detail::downcasted_kind<QK1>(lhs.common() / rhs.common());
}
template<QuantityKind QK, Dimensionless D>
[[nodiscard]] constexpr QuantityKind auto operator%(const QK& lhs, const D& rhs)
requires requires { lhs.common() % rhs; }
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename D::rep>::zero());
return detail::make_quantity_kind<QK>(lhs.common() % rhs);
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator%(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() % rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().number() != quantity_values<typename QK2::rep>::zero());
return detail::make_quantity_kind<QK1>(lhs.common() % rhs.common());
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
requires std::three_way_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
[[nodiscard]] constexpr auto operator<=>(const QK1& lhs, const QK2& rhs)
{
return lhs.common() <=> rhs.common();
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
requires std::equality_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
[[nodiscard]] constexpr bool operator==(const QK1& lhs, const QK2& rhs)
{
return lhs.common() == rhs.common();
}
// type traits
namespace detail {
template<typename K, typename U, typename Rep>
inline constexpr bool is_quantity_kind<quantity_kind<K, U, Rep>> = true;
} // namespace detail
} // namespace units
-232
View File
@@ -1,232 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/point_origin.h>
#include <units/quantity.h>
#include <compare>
// IWYU pragma: end_exports
#include <units/customization_points.h>
#include <units/generic/dimensionless.h>
namespace units {
/**
* @brief A statically unspecified quantity point origin
*
* An origin, unspecified in the type system, from which an absolute quantity is measured from.
*
* @tparam D a dimension of the quantity point (can be either a BaseDimension or a DerivedDimension)
*/
template<Dimension D>
struct dynamic_origin : point_origin<D> {
template<Dimension D2>
using rebind = dynamic_origin<D2>;
};
/**
* @brief A quantity point
*
* An absolute quantity measured from an origin.
*
* @tparam O a type that represents the origin from which the quantity point is measured from
* @tparam U a measurement unit of the quantity point
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep = double>
class quantity_point {
public:
using origin = O;
using quantity_type = quantity<typename origin::dimension, U, Rep>;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
static constexpr units::reference<dimension, unit> reference{};
private:
quantity_type q_{};
public:
quantity_point() = default;
quantity_point(const quantity_point&) = default;
quantity_point(quantity_point&&) = default;
template<typename T>
requires std::constructible_from<quantity_type, T>
constexpr explicit quantity_point(T&& t) : q_(std::forward<T>(t))
{
}
template<QuantityPointOf<origin> QP2>
requires std::convertible_to<typename QP2::quantity_type, quantity_type>
constexpr explicit(false) quantity_point(const QP2& qp) : q_(qp.relative())
{
}
template<QuantityPointLike QP>
constexpr explicit quantity_point(const QP& qp)
requires std::is_constructible_v<quantity_type, decltype(quantity_point_like_traits<QP>::relative(qp))> &&
equivalent<origin, typename quantity_point_like_traits<QP>::origin>
: q_(quantity_point_like_traits<QP>::relative(qp))
{
}
quantity_point& operator=(const quantity_point&) = default;
quantity_point& operator=(quantity_point&&) = default;
[[nodiscard]] constexpr quantity_type& relative() & noexcept { return q_; }
[[nodiscard]] constexpr const quantity_type& relative() const& noexcept { return q_; }
[[nodiscard]] constexpr quantity_type&& relative() && noexcept { return std::move(q_); }
[[nodiscard]] constexpr const quantity_type&& relative() const&& noexcept { return std::move(q_); }
[[nodiscard]] static constexpr quantity_point min() noexcept
requires requires { quantity_type::min(); }
{
return quantity_point(quantity_type::min());
}
[[nodiscard]] static constexpr quantity_point max() noexcept
requires requires { quantity_type::max(); }
{
return quantity_point(quantity_type::max());
}
constexpr quantity_point& operator++()
requires requires(quantity_type q) { ++q; }
{
++q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator++(int)
requires requires(quantity_type q) { q++; }
{
return quantity_point(q_++);
}
constexpr quantity_point& operator--()
requires requires(quantity_type q) { --q; }
{
--q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator--(int)
requires requires(quantity_type q) { q--; }
{
return quantity_point(q_--);
}
constexpr quantity_point& operator+=(const quantity_type& q)
requires requires { q_ += q; }
{
q_ += q;
return *this;
}
constexpr quantity_point& operator-=(const quantity_type& q)
requires requires(quantity_type q1, quantity_type q2) { q1 -= q2; }
{
q_ -= q;
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator+(const quantity_point& lhs, const Q& rhs)
requires requires(quantity_type q) { q + rhs; }
{
const auto q = lhs.relative() + rhs;
using q_type = decltype(q);
return quantity_point<rebind_point_origin_dimension<origin, typename q_type::dimension>, typename q_type::unit,
typename q_type::rep>(q);
}
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator+(const Q& lhs, const quantity_point& rhs)
requires requires { rhs + lhs; }
{
return rhs + lhs;
}
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator-(const quantity_point& lhs, const Q& rhs)
requires requires(quantity_type q) { q - rhs; }
{
const auto q = lhs.relative() - rhs;
using q_type = decltype(q);
return quantity_point<rebind_point_origin_dimension<origin, typename q_type::dimension>, typename q_type::unit,
typename q_type::rep>(q);
}
template<QuantityPointOf<origin> QP>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity_point& lhs, const QP& rhs)
requires requires(quantity_type q) { q - rhs.relative(); }
{
return lhs.relative() - rhs.relative();
}
template<QuantityPointOf<origin> QP>
requires std::three_way_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point& lhs, const QP& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<QuantityPointOf<origin> QP>
requires std::equality_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr bool operator==(const quantity_point& lhs, const QP& rhs)
{
return lhs.relative() == rhs.relative();
}
};
template<Representation Rep>
explicit quantity_point(Rep) -> quantity_point<dynamic_origin<dim_one>, one, Rep>;
template<Quantity Q>
explicit quantity_point(Q) -> quantity_point<dynamic_origin<typename Q::dimension>, typename Q::unit, typename Q::rep>;
template<QuantityLike Q>
explicit quantity_point(Q)
-> quantity_point<dynamic_origin<typename quantity_like_traits<Q>::dimension>, typename quantity_like_traits<Q>::unit,
typename quantity_like_traits<Q>::rep>;
template<QuantityPointLike QP>
explicit quantity_point(QP)
-> quantity_point<typename quantity_point_like_traits<QP>::origin, typename quantity_point_like_traits<QP>::unit,
typename quantity_point_like_traits<QP>::rep>;
namespace detail {
template<typename O, typename U, typename Rep>
inline constexpr bool is_quantity_point<quantity_point<O, U, Rep>> = true;
} // namespace detail
} // namespace units
@@ -1,202 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/quantity_kind.h>
#include <units/quantity_point.h>
#include <compare>
// IWYU pragma: end_exports
namespace units {
/**
* @brief A quantity point kind
*
* An absolute quantity kind measured from an origin.
*
* @tparam PK the point kind of quantity point
* @tparam U the measurement unit of the quantity point kind
* @tparam Rep the type to be used to represent values of the quantity point kind
*/
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep = double>
class quantity_point_kind {
public:
using point_kind_type = PK;
using kind_type = typename PK::base_kind;
using origin = typename point_kind_type::origin;
using quantity_kind_type = quantity_kind<kind_type, U, Rep>;
using quantity_type = typename quantity_kind_type::quantity_type;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
static constexpr units::reference<dimension, unit> reference{};
private:
quantity_kind_type qk_;
public:
quantity_point_kind() = default;
quantity_point_kind(const quantity_point_kind&) = default;
quantity_point_kind(quantity_point_kind&&) = default;
template<typename T>
requires std::constructible_from<quantity_kind_type, T>
constexpr explicit quantity_point_kind(T&& t) : qk_(std::forward<T>(t))
{
}
constexpr explicit quantity_point_kind(const quantity_point<origin, U, Rep>& qp) : qk_(qp.relative()) {}
constexpr explicit quantity_point_kind(quantity_point<origin, U, Rep>&& qp) : qk_(std::move(qp).relative()) {}
template<QuantityPointLike QP>
requires(std::constructible_from<quantity_point<origin, U, Rep>, QP>)
constexpr explicit quantity_point_kind(const QP& qp) : qk_(quantity_point_like_traits<QP>::relative(qp))
{
}
template<QuantityPointKindOf<point_kind_type> QPK2>
requires std::convertible_to<typename QPK2::quantity_kind_type, quantity_kind_type>
constexpr explicit(false) quantity_point_kind(const QPK2& qpk) : qk_(qpk.relative())
{
}
quantity_point_kind& operator=(const quantity_point_kind&) = default;
quantity_point_kind& operator=(quantity_point_kind&&) = default;
[[nodiscard]] constexpr quantity_kind_type& relative() & noexcept { return qk_; }
[[nodiscard]] constexpr const quantity_kind_type& relative() const& noexcept { return qk_; }
[[nodiscard]] constexpr quantity_kind_type&& relative() && noexcept { return std::move(qk_); }
[[nodiscard]] constexpr const quantity_kind_type&& relative() const&& noexcept { return std::move(qk_); }
[[nodiscard]] static constexpr quantity_point_kind min() noexcept
requires requires { quantity_kind_type::min(); }
{
return quantity_point_kind(quantity_kind_type::min());
}
[[nodiscard]] static constexpr quantity_point_kind max() noexcept
requires requires { quantity_kind_type::max(); }
{
return quantity_point_kind(quantity_kind_type::max());
}
constexpr quantity_point_kind& operator++()
requires requires(quantity_kind_type qk) { ++qk; }
{
++qk_;
return *this;
}
[[nodiscard]] constexpr quantity_point_kind operator++(int)
requires requires(quantity_kind_type qk) { qk++; }
{
return quantity_point_kind(qk_++);
}
constexpr quantity_point_kind& operator--()
requires requires(quantity_kind_type qk) { --qk; }
{
--qk_;
return *this;
}
[[nodiscard]] constexpr quantity_point_kind operator--(int)
requires requires(quantity_kind_type qk) { qk--; }
{
return quantity_point_kind(qk_--);
}
constexpr quantity_point_kind& operator+=(const quantity_kind_type& qk)
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 += qk2; }
{
qk_ += qk;
return *this;
}
constexpr quantity_point_kind& operator-=(const quantity_kind_type& qk)
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 -= qk2; }
{
qk_ -= qk;
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const quantity_point_kind& lhs, const QK& rhs)
requires requires(quantity_kind_type qk) { qk + rhs; }
{
return units::quantity_point_kind(lhs.relative() + rhs);
}
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const QK& lhs, const quantity_point_kind& rhs)
requires requires(quantity_kind_type qk) { lhs + qk; }
{
return units::quantity_point_kind(lhs + rhs.relative());
}
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator-(const quantity_point_kind& lhs, const QK& rhs)
requires requires(quantity_kind_type qk) { qk - rhs; }
{
return units::quantity_point_kind(lhs.relative() - rhs);
}
template<QuantityPointKindOf<point_kind_type> QPK>
[[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const QPK& rhs)
requires requires(quantity_kind_type qk) { qk - qk; }
{
return lhs.relative() - rhs.relative();
}
template<QuantityPointKindOf<point_kind_type> QPK>
requires std::three_way_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point_kind& lhs, const QPK& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<QuantityPointKindOf<point_kind_type> QPK>
requires std::equality_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
[[nodiscard]] friend constexpr bool operator==(const quantity_point_kind& lhs, const QPK& rhs)
{
return lhs.relative() == rhs.relative();
}
};
template<QuantityKind QK>
quantity_point_kind(QK)
-> quantity_point_kind<downcast_point_kind<typename QK::kind_type>, typename QK::unit, typename QK::rep>;
namespace detail {
template<typename PK, typename U, typename Rep>
inline constexpr bool is_quantity_point_kind<quantity_point_kind<PK, U, Rep>> = true;
} // namespace detail
} // namespace units
-92
View File
@@ -1,92 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/bits/math_concepts.h>
#include <units/bits/ratio_maths.h>
#include <cstdint>
// IWYU pragma: end_exports
#include <gsl/gsl-lite.hpp>
#include <array>
#include <compare>
#include <numeric>
namespace units {
struct ratio;
constexpr ratio inverse(const ratio& r);
/**
* @brief Provides compile-time rational arithmetic support.
*
* This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class
* are used as class NTTPs rather then a type template parameter kind.
*/
struct ratio {
std::intmax_t num;
std::intmax_t den;
constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num(n), den(d)
{
gsl_Expects(den != 0);
detail::normalize(num, den);
}
[[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default;
[[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; }
[[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den); }
[[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs)
{
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den};
}
[[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); }
[[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1));
}
[[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) { return lhs * inverse(rhs); }
};
[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num); }
[[nodiscard]] constexpr bool is_integral(const ratio& r) { return r.num % r.den == 0; }
// common_ratio
[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2)
{
const auto res = detail::gcd_frac(r1.num, r1.den, r2.num, r2.den);
return ratio(res[0], res[1]);
}
} // namespace units
-139
View File
@@ -1,139 +0,0 @@
// 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 <units/bits/basic_concepts.h>
#include <units/bits/dimension_op.h>
namespace units {
template<Dimension D, UnitOf<D> U, Representation Rep>
class quantity;
template<Dimension D, UnitOf<D> U>
struct reference;
namespace detail {
template<Dimension D, Reference R1, Reference R2>
using reference_multiply_impl = reference<D, downcast_unit<D, R1::mag * R2::mag / D::mag>>;
template<typename D, Reference R1, Reference R2>
using reference_divide_impl = reference<D, downcast_unit<D, R1::mag / R2::mag / D::mag>>;
} // namespace detail
template<Reference R1, Reference R2>
using reference_multiply =
detail::reference_multiply_impl<dimension_multiply<typename R1::dimension, typename R2::dimension>, R1, R2>;
template<Reference R1, Reference R2>
using reference_divide =
detail::reference_divide_impl<dimension_divide<typename R1::dimension, typename R2::dimension>, R1, R2>;
/**
* @brief The type for quantity references
*
* This type is intended to be used in the quantity references definition:
*
* @code{.cpp}
* namespace length_references {
*
* inline constexpr auto m = reference<dim_length, metre>{};
* inline constexpr auto km = reference<dim_length, kilometre>{};
*
* }
*
* namespace references {
*
* using namespace length_references;
*
* }
* @endcode
*
* Quantity references simplify quantity creation:
*
* @code{.cpp}
* using namespace units::isq::si::references;
*
* auto d = 123 * m;
* auto v = 70 * (km / h);
* @endcode
*
* Also, it is allowed to define custom quantity references from existing ones:
*
* @code{.cpp}
* constexpr auto Nm = N * m;
* constexpr auto mph = mi / h;
* @endcode
*
* The following syntaxes are not allowed:
* `2 / s`, `km * 3`, `s / 4`, `70 * km / h`.
*/
template<Dimension D, UnitOf<D> U>
struct reference {
using dimension = D;
using unit = U;
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<Reference R2>
[[nodiscard]] friend constexpr reference_multiply<reference, R2> operator*(reference, R2)
{
return {};
}
template<Reference R2>
[[nodiscard]] friend constexpr reference_divide<reference, R2> operator/(reference, R2)
{
return {};
}
template<Representation Rep>
[[nodiscard]] friend constexpr Quantity auto operator*(const Rep& lhs, reference)
{
return quantity<D, U, Rep>(lhs);
}
friend void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, reference) = delete;
template<Reference R2>
[[nodiscard]] friend constexpr bool operator==(reference, R2)
{
return false;
}
[[nodiscard]] friend constexpr bool operator==(reference, reference) { return true; }
};
// type traits
namespace detail {
template<typename D, typename U>
inline constexpr bool is_reference<reference<D, U>> = true;
} // namespace detail
} // namespace units
-183
View File
@@ -1,183 +0,0 @@
// 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
// IWYU pragma: begin_exports
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h>
#include <compare>
#include <cstddef>
#include <cstdint>
// IWYU pragma: end_exports
#include <gsl/gsl-lite.hpp>
namespace units {
namespace detail {
constexpr void validate_ascii_char([[maybe_unused]] char c) noexcept { gsl_Expects((c & 0x80) == 0); }
template<std::size_t N>
constexpr void validate_ascii_string([[maybe_unused]] const char (&s)[N + 1]) noexcept
{
#ifndef NDEBUG
if constexpr (N != 0)
for (size_t i = 0; i < N; ++i) validate_ascii_char(s[i]);
#endif
}
} // namespace detail
/**
* @brief A symbol text representation
*
* This class template is responsible for definition and handling of a symbol text
* representation. In the libary it is used to define symbols of units and prefixes.
* Each symbol can have two versions: Unicode and ASCI-only.
*
* @tparam StandardCharT Character type to be used for a Unicode representation
* @tparam N The size of a Unicode symbol
* @tparam M The size of the ASCII-only symbol
*/
template<typename StandardCharT, std::size_t N, std::size_t M>
struct basic_symbol_text {
basic_fixed_string<StandardCharT, N> standard_;
basic_fixed_string<char, M> ascii_;
constexpr basic_symbol_text(char std) noexcept : standard_(std), ascii_(std) { detail::validate_ascii_char(std); }
constexpr basic_symbol_text(StandardCharT std, char a) noexcept : standard_(std), ascii_(a)
{
detail::validate_ascii_char(a);
}
constexpr basic_symbol_text(const char (&std)[N + 1]) noexcept : standard_(std), ascii_(std)
{
detail::validate_ascii_string<N>(std);
}
constexpr basic_symbol_text(const basic_fixed_string<char, N>& std) noexcept : standard_(std), ascii_(std)
{
detail::validate_ascii_string<N>(std.data_);
}
constexpr basic_symbol_text(const StandardCharT (&std)[N + 1], const char (&ascii)[M + 1]) noexcept :
standard_(std), ascii_(ascii)
{
detail::validate_ascii_string<M>(ascii);
}
constexpr basic_symbol_text(const basic_fixed_string<StandardCharT, N>& std,
const basic_fixed_string<char, M>& ascii) noexcept :
standard_(std), ascii_(ascii)
{
detail::validate_ascii_string<M>(ascii.data_);
}
[[nodiscard]] constexpr auto& standard() { return standard_; }
[[nodiscard]] constexpr const auto& standard() const { return standard_; }
[[nodiscard]] constexpr auto& ascii() { return ascii_; }
[[nodiscard]] constexpr const auto& ascii() const { return ascii_; }
template<std::size_t N2, std::size_t M2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + M2> operator+(
const basic_symbol_text& lhs, const basic_symbol_text<StandardCharT, N2, M2>& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N + N2, M + M2>(lhs.standard() + rhs.standard(), lhs.ascii() + rhs.ascii());
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + N2> operator+(
const basic_symbol_text& lhs, const basic_fixed_string<StandardCharT, N2>& rhs) noexcept
{
return lhs + basic_symbol_text<StandardCharT, N2, N2>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + N2> operator+(
const basic_fixed_string<StandardCharT, N2>& lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N2, N2>(lhs) + rhs;
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2 - 1, M + N2 - 1> operator+(
const basic_symbol_text& lhs, const StandardCharT (&rhs)[N2]) noexcept
{
return lhs + basic_symbol_text<StandardCharT, N2 - 1, N2 - 1>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2 - 1, M + N2 - 1> operator+(
const StandardCharT (&lhs)[N2], const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N2 - 1, N2 - 1>(lhs) + rhs;
}
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + 1, M + 1> operator+(const basic_symbol_text& lhs,
StandardCharT rhs) noexcept
{
return lhs + basic_symbol_text<StandardCharT, 1, 1>(rhs);
}
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + 1, M + 1> operator+(
StandardCharT lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, 1, 1>(lhs) + rhs;
}
template<typename StandardCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr auto operator<=>(const basic_symbol_text& lhs,
const basic_symbol_text<StandardCharT2, N2, M2>& rhs) noexcept
{
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
if (const auto cmp = lhs.standard() <=> rhs.standard(); cmp != 0) return cmp;
UNITS_DIAGNOSTIC_POP
return lhs.ascii() <=> rhs.ascii();
}
template<typename StandardCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr bool operator==(const basic_symbol_text& lhs,
const basic_symbol_text<StandardCharT2, N2, M2>& rhs) noexcept
{
return lhs.standard() == rhs.standard() && lhs.ascii() == rhs.ascii();
}
};
basic_symbol_text(char) -> basic_symbol_text<char, 1, 1>;
template<typename StandardCharT>
basic_symbol_text(StandardCharT, char) -> basic_symbol_text<StandardCharT, 1, 1>;
template<std::size_t N>
basic_symbol_text(const char (&)[N]) -> basic_symbol_text<char, N - 1, N - 1>;
template<std::size_t N>
basic_symbol_text(const basic_fixed_string<char, N>&) -> basic_symbol_text<char, N, N>;
template<typename StandardCharT, std::size_t N, std::size_t M>
basic_symbol_text(const StandardCharT (&)[N], const char (&)[M]) -> basic_symbol_text<StandardCharT, N - 1, M - 1>;
template<typename StandardCharT, std::size_t N, std::size_t M>
basic_symbol_text(const basic_fixed_string<StandardCharT, N>&, const basic_fixed_string<char, M>&)
-> basic_symbol_text<StandardCharT, N, M>;
} // namespace units
-233
View File
@@ -1,233 +0,0 @@
// 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 <units/bits/derived_symbol_text.h>
#include <units/bits/external/downcasting.h>
#include <units/bits/external/type_traits.h>
// IWYU pragma: begin_exports
#include <units/bits/derived_scaled_unit.h>
#include <units/bits/external/fixed_string.h>
#include <units/magnitude.h>
#include <units/prefix.h>
#include <units/ratio.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
namespace units {
namespace detail {
template<typename>
inline constexpr bool can_be_prefixed = false;
} // namespace detail
/**
* @brief A common point for a hierarchy of units
*
* A unit is an entity defined and adopted by convention, with which any other quantity of
* the same kind can be compared to express the ratio of the second quantity to the first
* one as a number.
*
* All units of the same dimension can be convereted between each other. To allow this all of
* them are expressed as different ratios of the same one proprietary chosen reference unit
* (i.e. all length units are expressed in terms of meter, all mass units are expressed in
* terms of gram, ...)
*
* @tparam M a Magnitude representing the (relative) size of this unit
* @tparam U a unit to use as a reference for this dimension
*
* @note U cannot be constrained with Unit as for some specializations (i.e. named_unit)
* it gets the incomplete child's type with the CRTP idiom.
*/
template<Magnitude auto M, typename U>
struct scaled_unit : downcast_base<scaled_unit<M, U>> {
static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M;
using reference = U;
};
template<Dimension D, Magnitude auto M>
using downcast_unit = downcast<scaled_unit<M, typename dimension_unit<D>::reference>>;
template<Unit U1, Unit U2>
struct same_unit_reference : is_same<typename U1::reference, typename U2::reference> {};
/**
* @brief A named unit
*
* Defines a named (in most cases coherent) unit that is then passed to a dimension definition.
* A named unit may be composed with a prefix to create a prefixed_unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
*/
template<typename Child, basic_symbol_text Symbol>
struct named_unit : downcast_dispatch<Child, scaled_unit<mag<1>(), Child>> {
static constexpr auto symbol = Symbol;
};
/**
* @brief A named scaled unit
*
* Defines a new named unit that is a scaled version of another unit.
* A named unit may be composed with a prefix to create a prefixed_unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
* @tparam M the Magnitude by which to scale U
* @tparam U a reference unit to scale
*/
template<typename Child, basic_symbol_text Symbol, Magnitude auto M, Unit U>
struct named_scaled_unit : downcast_dispatch<Child, scaled_unit<M * U::mag, typename U::reference>> {
static constexpr auto symbol = Symbol;
};
/**
* @brief A prefixed unit
*
* Defines a new unit that is a scaled version of another unit by the provided prefix. It is
* only possible to create such a unit if the given prefix type matches the one defined in a
* reference unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam P prefix to be appied to the reference unit
* @tparam U reference unit
*/
template<typename Child, Prefix P, NamedUnit U>
requires detail::can_be_prefixed<U>
struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::mag * U::mag, typename U::reference>> {
static constexpr auto symbol = P::symbol + U::symbol;
};
/**
* @brief A coherent unit of a derived quantity
*
* Defines a new coherent unit of a derived quantity. It should be passed as a coherent unit
* in the dimension's definition for such a quantity.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/
template<typename Child>
struct derived_unit : downcast_dispatch<Child, scaled_unit<mag<1>(), Child>> {};
/**
* @brief A unit with a deduced ratio and symbol
*
* Defines a new unit with a deduced ratio and symbol based on the recipe from the provided
* derived dimension. The number and order of provided units should match the recipe of the
* derived dimension. All of the units provided should also be a named ones so it is possible
* to create a deduced symbol text.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Dim a derived dimension recipe to use for deduction
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, NamedUnit U, NamedUnit... URest>
requires detail::compatible_units<typename Dim::recipe, U, URest...>
struct derived_scaled_unit : downcast_dispatch<Child, detail::derived_scaled_unit<Dim, U, URest...>> {
static constexpr auto symbol = detail::derived_symbol_text<Dim, U, URest...>();
};
/**
* @brief An aliased named unit
*
* Defines a named alias for another unit. It is useful to assign alternative names and symbols
* to the already predefined units (i.e. "tonne" for "megagram").
* A alias unit may be composed with a prefix to create a prefixed_alias_unit.
*
* @tparam U Unit for which an alias is defined
* @tparam Symbol a short text representation of the unit
*/
template<Unit U, basic_symbol_text Symbol>
struct alias_unit : U {
static constexpr auto symbol = Symbol;
};
/**
* @brief A prefixed alias unit
*
* Defines a new unit that is an alias for a scaled version of another unit by the provided
* prefix. It is only possible to create such a unit if the given prefix type matches the one
* defined in a reference unit.
*
* @tparam U Unit for which an alias is defined
* @tparam P prefix to be appied to the reference unit
* @tparam AU reference alias unit
*/
template<Unit U, Prefix P, AliasUnit AU>
requires(!AliasUnit<U>)
struct prefixed_alias_unit : U {
static constexpr auto symbol = P::symbol + AU::symbol;
};
/**
* @brief Unknown coherent unit
*
* Used as a coherent unit of an unknown dimension.
*/
template<Exponent... Es>
struct unknown_coherent_unit :
downcast_dispatch<unknown_coherent_unit<Es...>,
scaled_unit<detail::absolute_magnitude(exponent_list<Es...>()), unknown_coherent_unit<Es...>>> {};
namespace detail {
template<typename Child, basic_symbol_text Symbol>
void is_named_impl(const volatile named_unit<Child, Symbol>*);
template<typename Child, basic_symbol_text Symbol, Magnitude auto M, typename U>
void is_named_impl(const volatile named_scaled_unit<Child, Symbol, M, U>*);
template<typename Child, typename P, typename U>
void is_named_impl(const volatile prefixed_unit<Child, P, U>*);
template<typename U, basic_symbol_text Symbol>
void is_named_impl(const volatile alias_unit<U, Symbol>*);
template<typename U, typename P, typename AU>
void is_named_impl(const volatile prefixed_alias_unit<U, P, AU>*);
template<Unit U>
inline constexpr bool is_named<U> = requires(U* u) { is_named_impl(u); };
template<typename Child, basic_symbol_text Symbol>
void can_be_prefixed_impl(const volatile named_unit<Child, Symbol>*);
template<typename Child, basic_symbol_text Symbol, Magnitude auto M, typename U>
void can_be_prefixed_impl(const volatile named_scaled_unit<Child, Symbol, M, U>*);
template<typename U, basic_symbol_text Symbol>
void can_be_prefixed_impl(const volatile alias_unit<U, Symbol>*);
template<Unit U>
inline constexpr bool can_be_prefixed<U> = requires(U* u) { can_be_prefixed_impl(u); };
template<Magnitude auto M, typename U>
inline constexpr bool can_be_prefixed<scaled_unit<M, U>> = can_be_prefixed<typename U::reference>;
} // namespace detail
} // namespace units
+22 -20
View File
@@ -23,36 +23,38 @@
cmake_minimum_required(VERSION 3.19)
# systems
add_subdirectory(angular)
add_subdirectory(iec80000)
add_subdirectory(isq)
add_subdirectory(isq-iec80000)
add_subdirectory(isq-natural)
add_subdirectory(isq_angle)
add_subdirectory(natural)
add_subdirectory(si)
add_subdirectory(si-cgs)
add_subdirectory(si-fps)
add_subdirectory(si-hep)
add_subdirectory(si-iau)
add_subdirectory(si-imperial)
add_subdirectory(si-international)
add_subdirectory(si-typographic)
add_subdirectory(si-uscs)
add_subdirectory(cgs)
add_subdirectory(hep)
add_subdirectory(iau)
add_subdirectory(imperial)
add_subdirectory(international)
add_subdirectory(typographic)
add_subdirectory(usc)
# wrapper for all the systems
add_library(mp-units-systems INTERFACE)
target_link_libraries(
mp-units-systems
INTERFACE
mp-units::angular
mp-units::iec80000
mp-units::isq
mp-units::isq-iec80000
mp-units::isq-natural
mp-units::isq_angle
mp-units::natural
mp-units::si
mp-units::si-cgs
mp-units::si-fps
mp-units::si-hep
mp-units::si-iau
mp-units::si-imperial
mp-units::si-international
mp-units::si-typographic
mp-units::si-uscs
mp-units::cgs
mp-units::hep
mp-units::iau
mp-units::imperial
mp-units::international
mp-units::typographic
mp-units::usc
)
add_library(mp-units::systems ALIAS mp-units-systems)
set_target_properties(mp-units-systems PROPERTIES EXPORT_NAME systems)
@@ -22,7 +22,4 @@
cmake_minimum_required(VERSION 3.19)
add_units_module(
si-uscs DEPENDENCIES mp-units::si HEADERS include/units/isq/si/uscs/length.h include/units/isq/si/uscs/pressure.h
include/units/isq/si/uscs/uscs.h
)
add_units_module(angular DEPENDENCIES mp-units::isq HEADERS include/mp-units/systems/angular/angular.h)
@@ -0,0 +1,55 @@
// 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/dimension.h>
#include <mp-units/quantity_spec.h>
#include <mp-units/unit.h>
namespace mp_units::angular {
// clang-format off
inline constexpr struct dim_angle : base_dimension<"A"> {} dim_angle;
QUANTITY_SPEC(angle, dim_angle);
QUANTITY_SPEC(solid_angle, pow<2>(angle));
inline constexpr struct radian : named_unit<"rad", kind_of<angle>> {} radian;
inline constexpr struct revolution : named_unit<"rev", mag<2> * mag_pi * radian> {} revolution;
inline constexpr struct degree : named_unit<basic_symbol_text{"°", "deg"}, mag<ratio{1, 360}> * revolution> {} degree;
inline constexpr struct gradian : named_unit<basic_symbol_text{"ᵍ", "grad"}, mag<ratio{1, 400}> * revolution> {} gradian;
inline constexpr struct steradian : named_unit<"sr", square(radian)> {} steradian;
// clang-format on
namespace unit_symbols {
inline constexpr auto rad = radian;
inline constexpr auto rev = revolution;
inline constexpr auto deg = degree;
inline constexpr auto grad = gradian;
inline constexpr auto sr = steradian;
inline constexpr auto rad2 = square(radian);
inline constexpr auto deg2 = square(degree);
} // namespace unit_symbols
} // namespace mp_units::angular
+25
View File
@@ -0,0 +1,25 @@
# 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.
cmake_minimum_required(VERSION 3.19)
add_units_module(cgs DEPENDENCIES mp-units::si HEADERS include/mp-units/systems/cgs/cgs.h)
@@ -0,0 +1,63 @@
// 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/systems/si/units.h>
#include <mp-units/unit.h>
namespace mp_units::cgs {
// clang-format off
inline constexpr struct centimetre : decltype(si::centi<si::metre>) {} centimetre;
inline constexpr struct gram : decltype(si::gram) {} gram;
inline constexpr struct second : decltype(si::second) {} second;
inline constexpr struct gal : named_unit<"Gal", centimetre / square(second)> {} gal;
inline constexpr struct dyne : named_unit<"dyn", gram * centimetre / square(second)> {} dyne;
inline constexpr struct erg : named_unit<"erg", dyne * centimetre> {} erg;
inline constexpr struct barye : named_unit<"Ba", gram / (centimetre * square(second))> {} barye;
inline constexpr struct poise : named_unit<"P", gram / (centimetre * second)> {} poise;
inline constexpr struct stokes : named_unit<"St", square(centimetre) / second> {} stokes;
inline constexpr struct kayser : named_unit<"K", 1 / centimetre> {} kayser;
// clang-format on
namespace unit_symbols {
inline constexpr auto cm = centimetre;
inline constexpr auto g = gram;
inline constexpr auto s = second;
inline constexpr auto Gal = gal;
inline constexpr auto dyn = dyne;
inline constexpr auto Ba = barye;
inline constexpr auto P = poise;
inline constexpr auto St = stokes;
inline constexpr auto K = kayser;
// commonly used squared and cubic units
inline constexpr auto cm2 = square(centimetre);
inline constexpr auto cm3 = cubic(centimetre);
inline constexpr auto s2 = square(second);
inline constexpr auto s3 = cubic(second);
} // namespace unit_symbols
} // namespace mp_units::cgs
@@ -22,8 +22,4 @@
cmake_minimum_required(VERSION 3.19)
add_units_module(
si-hep DEPENDENCIES mp-units::si
HEADERS include/units/isq/si/hep/area.h include/units/isq/si/hep/energy.h include/units/isq/si/hep/hep.h
include/units/isq/si/hep/mass.h include/units/isq/si/hep/momentum.h
)
add_units_module(hep DEPENDENCIES mp-units::si HEADERS include/mp-units/systems/hep/hep.h)
@@ -0,0 +1,100 @@
// The MIT License (MIT)
//
// Copyright (c) 2021 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/systems/si/si.h>
#include <mp-units/unit.h>
namespace mp_units::hep {
// energy
using si::electronvolt;
// clang-format off
// area
// effective cross-sectional area according to EU council directive 80/181/EEC
// https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10
// https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de
inline constexpr struct barn : named_unit<"b", mag_power<10, -28> * square(si::metre)> {} barn;
// mass
inline constexpr struct electron_mass : named_unit<"m_e", mag<ratio{9'109'383'701'528, 1'000'000'000'000}> * mag_power<10, -31> * si::kilogram> {} electron_mass;
inline constexpr struct proton_mass : named_unit<"m_p", mag<ratio{1'672'621'923'695, 1'000'000'000'000}> * mag_power<10, -27> * si::kilogram> {} proton_mass;
inline constexpr struct neutron_mass : named_unit<"m_n", mag<ratio{1'674'927'498'049, 1'000'000'000'000}> * mag_power<10, -27> * si::kilogram> {} neutron_mass;
// speed
inline constexpr struct speed_of_light : decltype(si::si2019::speed_of_light_in_vacuum) {} speed_of_light;
// clang-format on
namespace unit_symbols {
using si::unit_symbols::eV;
inline constexpr auto qeV = si::quecto<electronvolt>;
inline constexpr auto reV = si::ronto<electronvolt>;
inline constexpr auto yeV = si::yocto<electronvolt>;
inline constexpr auto zeV = si::zepto<electronvolt>;
inline constexpr auto aeV = si::atto<electronvolt>;
inline constexpr auto feV = si::femto<electronvolt>;
inline constexpr auto peV = si::pico<electronvolt>;
inline constexpr auto neV = si::nano<electronvolt>;
inline constexpr auto ueV = si::micro<electronvolt>;
inline constexpr auto meV = si::milli<electronvolt>;
inline constexpr auto ceV = si::centi<electronvolt>;
inline constexpr auto deV = si::deci<electronvolt>;
inline constexpr auto daeV = si::deca<electronvolt>;
inline constexpr auto heV = si::hecto<electronvolt>;
inline constexpr auto keV = si::kilo<electronvolt>;
inline constexpr auto MeV = si::mega<electronvolt>;
inline constexpr auto GeV = si::giga<electronvolt>;
inline constexpr auto TeV = si::tera<electronvolt>;
inline constexpr auto PeV = si::peta<electronvolt>;
inline constexpr auto EeV = si::exa<electronvolt>;
inline constexpr auto ZeV = si::zetta<electronvolt>;
inline constexpr auto YeV = si::yotta<electronvolt>;
inline constexpr auto ReV = si::ronna<electronvolt>;
inline constexpr auto QeV = si::quetta<electronvolt>;
inline constexpr auto qb = si::quecto<barn>;
inline constexpr auto rb = si::ronto<barn>;
inline constexpr auto yb = si::yocto<barn>;
inline constexpr auto zb = si::zepto<barn>;
inline constexpr auto ab = si::atto<barn>;
inline constexpr auto fb = si::femto<barn>;
inline constexpr auto pb = si::pico<barn>;
inline constexpr auto nb = si::nano<barn>;
inline constexpr auto ub = si::micro<barn>;
inline constexpr auto mb = si::milli<barn>;
inline constexpr auto b = barn;
inline constexpr auto m_e = electron_mass;
inline constexpr auto m_p = proton_mass;
inline constexpr auto m_n = neutron_mass;
inline constexpr auto c = speed_of_light;
inline constexpr auto c2 = square(speed_of_light);
} // namespace unit_symbols
} // namespace mp_units::hep
+25
View File
@@ -0,0 +1,25 @@
# 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.
cmake_minimum_required(VERSION 3.19)
add_units_module(iau DEPENDENCIES mp-units::si HEADERS include/mp-units/systems/iau/iau.h)
@@ -0,0 +1,97 @@
// 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/systems/si/constants.h>
#include <mp-units/systems/si/units.h>
#include <mp-units/unit.h>
namespace mp_units::iau {
// https://en.wikipedia.org/wiki/Astronomical_system_of_units
// clang-format off
// time
inline constexpr struct day : named_unit<"D", si::day> {} day;
inline constexpr struct Julian_year : named_unit<"a", mag<ratio{365'25, 100}> * day> {} Julian_year;
// mass
// https://en.wikipedia.org/wiki/Solar_mass
// TODO What is the official mass of sun (every source in the Internet provides a different value)
inline constexpr struct solar_mass : named_unit<basic_symbol_text{"M_☉", "M_SUN"}, mag<ratio{198'847, 100'000}> * mag_power<10, 30> * si::kilogram> {} solar_mass;
inline constexpr struct Jupiter_mass : named_unit<"M_JUP", mag<ratio{1'898, 1'000}> * mag_power<10, 27> * si::kilogram> {} Jupiter_mass;
inline constexpr struct Earth_mass : named_unit<"M_EARTH", mag<ratio{59'742, 10'000}> * mag_power<10, 24> * si::kilogram> {} Earth_mass;
// length
inline constexpr struct astronomical_unit : decltype(si::astronomical_unit) {} astronomical_unit;
// https://en.wikipedia.org/wiki/Lunar_distance_(astronomy)
inline constexpr struct lunar_distance : named_unit<"LD", mag<384'399> * si::kilo<si::metre>> {} lunar_distance;
// https://en.wikipedia.org/wiki/Light-year
inline constexpr struct light_year : named_unit<"ly", mag<9'460'730'472'580'800> * si::metre> {} light_year;
// https://en.wikipedia.org/wiki/Parsec
inline constexpr struct parsec : named_unit<"pc", astronomical_unit / (mag<ratio{1, 60 * 60}> * si::degree)> {} parsec;
// https://en.wikipedia.org/wiki/Angstrom
inline constexpr struct angstrom : named_unit<basic_symbol_text{"Å", "A"}, mag_power<10, -10> * si::metre> {} angstrom;
// selected constants
// https://en.wikipedia.org/wiki/Astronomical_constant
inline constexpr struct gaussian_gravitational_constant :
named_unit<"k", mag<ratio{1'720'209'895, 100'000'000'000}> * pow<3, 2>(astronomical_unit) / pow<1,2>(solar_mass) / day> {} gaussian_gravitational_constant;
inline constexpr struct speed_of_light :
named_unit<basic_symbol_text{"c₀", "c_0"}, si::si2019::speed_of_light_in_vacuum> {} speed_of_light;
inline constexpr struct constant_of_gravitation :
named_unit<"G", mag<ratio{667'430, 100'000}> * mag_power<10, -11> * cubic(si::metre) / si::kilogram / square(si::second)> {} constant_of_gravitation;
inline constexpr struct hubble_constant :
named_unit<basic_symbol_text{"H₀", "H_0"}, mag<ratio{701, 10}> * si::kilo<si::metre> / si::second / si::mega<parsec>> {} hubble_constant;
// clang-format on
namespace unit_symbols {
inline constexpr auto D = day;
inline constexpr auto a = Julian_year;
inline constexpr auto M_SUN = solar_mass;
inline constexpr auto M_JUP = Jupiter_mass;
inline constexpr auto M_EARTH = Earth_mass;
inline constexpr auto au = astronomical_unit;
inline constexpr auto LD = lunar_distance;
inline constexpr auto ly = light_year;
inline constexpr auto pc = parsec;
inline constexpr auto A = angstrom;
inline constexpr auto k = gaussian_gravitational_constant;
inline constexpr auto c_0 = speed_of_light;
inline constexpr auto G = constant_of_gravitation;
inline constexpr auto H_0 = hubble_constant;
} // namespace unit_symbols
} // namespace mp_units::iau
@@ -23,9 +23,9 @@
cmake_minimum_required(VERSION 3.19)
add_units_module(
si-international
DEPENDENCIES mp-units::si
HEADERS include/units/isq/si/international/area.h include/units/isq/si/international/international.h
include/units/isq/si/international/length.h include/units/isq/si/international/speed.h
include/units/isq/si/international/volume.h
iec80000
DEPENDENCIES mp-units::isq mp-units::si
HEADERS include/mp-units/systems/iec80000/binary_prefixes.h include/mp-units/systems/iec80000/quantities.h
include/mp-units/systems/iec80000/iec80000.h include/mp-units/systems/iec80000/unit_symbols.h
include/mp-units/systems/iec80000/units.h
)
@@ -0,0 +1,49 @@
// 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/unit.h>
namespace mp_units::iec80000 {
// clang-format off
template<PrefixableUnit auto U> struct kibi_ : prefixed_unit<"Ki", mag_power<2, 10>, U> {};
template<PrefixableUnit auto U> struct mebi_ : prefixed_unit<"Mi", mag_power<2, 20>, U> {};
template<PrefixableUnit auto U> struct gibi_ : prefixed_unit<"Gi", mag_power<2, 30>, U> {};
template<PrefixableUnit auto U> struct tebi_ : prefixed_unit<"Ti", mag_power<2, 40>, U> {};
template<PrefixableUnit auto U> struct pebi_ : prefixed_unit<"Pi", mag_power<2, 50>, U> {};
template<PrefixableUnit auto U> struct exbi_ : prefixed_unit<"Ei", mag_power<2, 60>, U> {};
template<PrefixableUnit auto U> struct zebi_ : prefixed_unit<"Zi", mag_power<2, 70>, U> {};
template<PrefixableUnit auto U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U> {};
template<PrefixableUnit auto U> inline constexpr kibi_<U> kibi;
template<PrefixableUnit auto U> inline constexpr mebi_<U> mebi;
template<PrefixableUnit auto U> inline constexpr gibi_<U> gibi;
template<PrefixableUnit auto U> inline constexpr tebi_<U> tebi;
template<PrefixableUnit auto U> inline constexpr pebi_<U> pebi;
template<PrefixableUnit auto U> inline constexpr exbi_<U> exbi;
template<PrefixableUnit auto U> inline constexpr zebi_<U> zebi;
template<PrefixableUnit auto U> inline constexpr yobi_<U> yobi;
// clang-format on
} // namespace mp_units::iec80000
@@ -23,8 +23,8 @@
#pragma once
// IWYU pragma: begin_exports
#include <units/isq/iec80000/modulation_rate.h>
#include <units/isq/iec80000/storage_capacity.h>
#include <units/isq/iec80000/traffic_intensity.h>
#include <units/isq/iec80000/transfer_rate.h>
#include <mp-units/systems/iec80000/binary_prefixes.h>
#include <mp-units/systems/iec80000/quantities.h>
#include <mp-units/systems/iec80000/unit_symbols.h>
#include <mp-units/systems/iec80000/units.h>
// IWYU pragma: end_exports
@@ -0,0 +1,73 @@
// 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/dimension.h>
#include <mp-units/quantity_spec.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
namespace mp_units::iec80000 {
// dimensions of base quantities
// clang-format off
inline constexpr struct dim_traffic_intensity : base_dimension<"A"> {} dim_traffic_intensity;
// clang-format on
// quantities
QUANTITY_SPEC(traffic_intensity, dim_traffic_intensity);
QUANTITY_SPEC(traffic_offered_intensity, traffic_intensity);
QUANTITY_SPEC(traffic_carried_intensity, traffic_intensity);
inline constexpr auto traffic_load = traffic_carried_intensity;
QUANTITY_SPEC(mean_queue_length, dimensionless);
QUANTITY_SPEC(loss_probability, dimensionless);
QUANTITY_SPEC(waiting_probability, dimensionless);
QUANTITY_SPEC(call_intensity, 1 / isq::duration);
inline constexpr auto calling_rate = call_intensity;
QUANTITY_SPEC(completed_call_intensity, call_intensity);
QUANTITY_SPEC(storage_capacity, dimensionless, is_kind);
inline constexpr auto storage_size = storage_capacity;
QUANTITY_SPEC(equivalent_binary_storage_capacity, storage_capacity);
QUANTITY_SPEC(transfer_rate, storage_capacity / isq::duration);
QUANTITY_SPEC(period_of_data_elements, isq::period, 1 / transfer_rate);
QUANTITY_SPEC(binary_digit_rate, transfer_rate);
inline constexpr auto bit_rate = binary_digit_rate;
QUANTITY_SPEC(period_of_binary_digits, isq::period, 1 / binary_digit_rate);
inline constexpr auto bit_period = period_of_binary_digits;
QUANTITY_SPEC(equivalent_binary_digit_rate, binary_digit_rate);
inline constexpr auto equivalent_bit_rate = bit_rate;
QUANTITY_SPEC(modulation_rate, 1 / isq::duration);
inline constexpr auto line_digit_rate = modulation_rate;
QUANTITY_SPEC(quantizing_distortion_power, isq::power);
QUANTITY_SPEC(carrier_power, isq::power);
QUANTITY_SPEC(signal_energy_per_binary_digit, carrier_power* period_of_binary_digits);
QUANTITY_SPEC(error_probability, dimensionless);
QUANTITY_SPEC(Hamming_distance, dimensionless);
QUANTITY_SPEC(clock_frequency, isq::frequency);
inline constexpr auto clock_rate = clock_frequency;
QUANTITY_SPEC(decision_content, dimensionless);
// TODO how to model information_content and the following quantities???
// QUANTITY_SPEC(information_content, ...);
} // namespace mp_units::iec80000
@@ -0,0 +1,109 @@
// 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/systems/iec80000/binary_prefixes.h>
#include <mp-units/systems/iec80000/units.h>
#include <mp-units/systems/si/prefixes.h>
namespace mp_units::iec80000::unit_symbols {
// bit
inline constexpr auto kbit = si::kilo<bit>;
inline constexpr auto Mbit = si::mega<bit>;
inline constexpr auto Gbit = si::giga<bit>;
inline constexpr auto Tbit = si::tera<bit>;
inline constexpr auto Pbit = si::peta<bit>;
inline constexpr auto Ebit = si::exa<bit>;
inline constexpr auto Zbit = si::zetta<bit>;
inline constexpr auto Ybit = si::yotta<bit>;
inline constexpr auto Rbit = si::ronna<bit>;
inline constexpr auto Qbit = si::quetta<bit>;
inline constexpr auto Kibit = kibi<bit>;
inline constexpr auto Mibit = mebi<bit>;
inline constexpr auto Gibit = gibi<bit>;
inline constexpr auto Tibit = tebi<bit>;
inline constexpr auto Pibit = pebi<bit>;
inline constexpr auto Eibit = exbi<bit>;
// octet
inline constexpr auto o = octet;
inline constexpr auto ko = si::kilo<octet>;
inline constexpr auto Mo = si::mega<octet>;
inline constexpr auto Go = si::giga<octet>;
inline constexpr auto To = si::tera<octet>;
inline constexpr auto Po = si::peta<octet>;
inline constexpr auto Eo = si::exa<octet>;
inline constexpr auto Zo = si::zetta<octet>;
inline constexpr auto Yo = si::yotta<octet>;
inline constexpr auto Ro = si::ronna<octet>;
inline constexpr auto Qo = si::quetta<octet>;
inline constexpr auto Kio = kibi<octet>;
inline constexpr auto Mio = mebi<octet>;
inline constexpr auto Gio = gibi<octet>;
inline constexpr auto Tio = tebi<octet>;
inline constexpr auto Pio = pebi<octet>;
inline constexpr auto Eio = exbi<octet>;
// byte
inline constexpr auto B = byte;
inline constexpr auto kB = si::kilo<byte>;
inline constexpr auto MB = si::mega<byte>;
inline constexpr auto GB = si::giga<byte>;
inline constexpr auto TB = si::tera<byte>;
inline constexpr auto PB = si::peta<byte>;
inline constexpr auto EB = si::exa<byte>;
inline constexpr auto ZB = si::zetta<byte>;
inline constexpr auto YB = si::yotta<byte>;
inline constexpr auto RB = si::ronna<byte>;
inline constexpr auto QB = si::quetta<byte>;
inline constexpr auto KiB = kibi<byte>;
inline constexpr auto MiB = mebi<byte>;
inline constexpr auto GiB = gibi<byte>;
inline constexpr auto TiB = tebi<byte>;
inline constexpr auto PiB = pebi<byte>;
inline constexpr auto EiB = exbi<byte>;
// baud
inline constexpr auto Bd = baud;
inline constexpr auto kBd = si::kilo<baud>;
inline constexpr auto MBd = si::mega<baud>;
inline constexpr auto GBd = si::giga<baud>;
inline constexpr auto TBd = si::tera<baud>;
inline constexpr auto PBd = si::peta<baud>;
inline constexpr auto EBd = si::exa<baud>;
inline constexpr auto ZBd = si::zetta<baud>;
inline constexpr auto YBd = si::yotta<baud>;
inline constexpr auto RBd = si::ronna<baud>;
inline constexpr auto QBd = si::quetta<baud>;
// erlang
// TODO do we need prefixed versions of Erlang?
inline constexpr auto E = erlang;
} // namespace mp_units::iec80000::unit_symbols
@@ -22,14 +22,18 @@
#pragma once
#include <units/concepts.h>
#include <mp-units/systems/iec80000/quantities.h>
#include <mp-units/systems/si/units.h>
#include <mp-units/unit.h>
namespace units::isq {
namespace mp_units::iec80000 {
template<Unit U>
struct dim_luminous_intensity : base_dimension<"J", U> {};
// clang-format off
inline constexpr struct erlang : named_unit<"E", kind_of<traffic_intensity>> {} erlang;
inline constexpr struct bit : named_unit<"bit", one, kind_of<storage_capacity>> {} bit;
inline constexpr struct octet : named_unit<"o", mag<8> * bit> {} octet;
inline constexpr struct byte : named_unit<"B", mag<8> * bit> {} byte;
inline constexpr struct baud : named_unit<"Bd", 1 / si::second, kind_of<modulation_rate>> {} baud;
// clang-format on
template<typename T>
concept LuminousIntensity = QuantityOfT<T, dim_luminous_intensity>;
} // namespace units::isq
} // namespace mp_units::iec80000
@@ -23,6 +23,5 @@
cmake_minimum_required(VERSION 3.19)
add_units_module(
si-imperial DEPENDENCIES mp-units::si HEADERS include/units/isq/si/imperial/imperial.h
include/units/isq/si/imperial/length.h
imperial DEPENDENCIES mp-units::si mp-units::international HEADERS include/mp-units/systems/imperial/imperial.h
)
@@ -0,0 +1,98 @@
// 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/systems/international/international.h>
#include <mp-units/unit.h>
namespace mp_units::imperial {
using namespace international;
// clang-format off
// https://en.wikipedia.org/wiki/Imperial_units#Length
inline constexpr struct hand : named_unit<"hh", mag<ratio{1, 3}> * foot> {} hand;
inline constexpr struct barleycorn : named_unit<"Bc", mag<ratio(1, 3)> * inch> {} barleycorn;
inline constexpr struct thou : named_unit<"th", mag<ratio{1, 12'000}> * foot> {} thou;
inline constexpr struct chain : named_unit<"ch", mag<22> * yard> {} chain;
inline constexpr struct furlong : named_unit<"fur", mag<10> * chain> {} furlong;
// maritime units
inline constexpr struct cable : named_unit<"cb", mag<ratio{1, 10}> * nautical_mile> {} cable;
inline constexpr struct fathom : named_unit<"ftm", mag<ratio{1, 1000}> * nautical_mile> {} fathom;
// survey
inline constexpr struct link : named_unit<"li", mag<ratio{1, 100}> * chain> {} link;
inline constexpr struct rod : named_unit<"rd", mag<25> * link> {} rod;
// https://en.wikipedia.org/wiki/Imperial_units#Area
inline constexpr struct perch : decltype(square(rod)) {} perch;
inline constexpr struct rood : decltype(mag<40> * perch) {} rood;
inline constexpr struct acre : decltype(mag<4> * rood) {} acre;
// https://en.wikipedia.org/wiki/Imperial_units#Volume
inline constexpr struct gallon : named_unit<"gal", mag<ratio{454'609, 100'000}> * si::litre> {} gallon;
inline constexpr struct quart : named_unit<"qt", mag<ratio{1, 4}> * gallon> {} quart;
inline constexpr struct pint : named_unit<"pt", mag<ratio{1, 2}> * quart> {} pint;
inline constexpr struct gill : named_unit<"gi", mag<ratio{1, 4}> * pint> {} gill;
inline constexpr struct fluid_ounce : named_unit<"fl oz", mag<ratio{1, 5}> * gill> {} fluid_ounce;
// https://en.wikipedia.org/wiki/Avoirdupois_system#Post-Elizabethan_units
inline constexpr auto drachm = dram;
inline constexpr struct stone : named_unit<"st", mag<14> * pound> {} stone;
inline constexpr struct quarter : named_unit<"qr", mag<2> * stone> {} quarter;
inline constexpr struct long_hundredweight : named_unit<"cwt", mag<8> * stone> {} long_hundredweight;
inline constexpr struct ton : named_unit<"t", mag<2'240> * pound> {} ton;
inline constexpr auto long_ton = ton;
// clang-format on
namespace unit_symbols {
using namespace international::unit_symbols;
inline constexpr auto hh = hand;
inline constexpr auto Bc = barleycorn;
inline constexpr auto th = thou;
inline constexpr auto ch = chain;
inline constexpr auto fur = furlong;
inline constexpr auto cb = cable;
inline constexpr auto ftm = fathom;
inline constexpr auto li = link;
inline constexpr auto rd = rod;
inline constexpr auto gal = gallon;
inline constexpr auto qt = quart;
inline constexpr auto pt = pint;
inline constexpr auto gi = gill;
inline constexpr auto fl_oz = fluid_ounce;
inline constexpr auto st = stone;
inline constexpr auto qr = quarter;
inline constexpr auto cwt = long_hundredweight;
inline constexpr auto t = ton;
} // namespace unit_symbols
} // namespace mp_units::imperial
+25
View File
@@ -0,0 +1,25 @@
# 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.
cmake_minimum_required(VERSION 3.19)
add_units_module(international DEPENDENCIES mp-units::si HEADERS include/mp-units/systems/international/international.h)
@@ -0,0 +1,101 @@
// 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/systems/si/si.h>
#include <mp-units/unit.h>
namespace mp_units::international {
// clang-format off
// mass
inline constexpr struct pound : named_unit<"lb", mag<ratio{45'359'237, 100'000'000}> * si::kilogram> {} pound;
inline constexpr struct ounce : named_unit<"oz", mag<ratio(1, 16)> * pound> {} ounce;
inline constexpr struct dram : named_unit<"dr", mag<ratio(1, 16)> * ounce> {} dram;
inline constexpr struct grain : named_unit<"gr", mag<ratio(1, 7'000)> * pound> {} grain;
// length
// https://en.wikipedia.org/wiki/United_States_customary_units#Length
inline constexpr struct yard : named_unit<"yd", mag<ratio{9'144, 10'000}> * si::metre> {} yard;
inline constexpr struct foot : named_unit<"ft", mag<ratio{1, 3}> * yard> {} foot;
inline constexpr struct inch : named_unit<"in", mag<ratio{1, 12}> * foot> {} inch;
inline constexpr struct pica : named_unit<"P", mag<ratio{1, 6}> * inch> {} pica;
inline constexpr struct point : named_unit<"p", mag<ratio{1, 12}> * pica> {} point;
inline constexpr struct mil : named_unit<"mil", mag<ratio{1, 1'000}> * inch> {} mil;
inline constexpr struct twip : named_unit<"twip", mag<ratio{1, 20}> * point> {} twip;
inline constexpr struct mile : named_unit<"mi", mag<1760> * yard> {} mile;
inline constexpr struct league : named_unit<"le", mag<3> * mile> {} league;
inline constexpr struct nautical_mile : named_unit<"nmi", mag<1852> * si::metre> {} nautical_mile;
// speed
inline constexpr struct knot : named_unit<"kn", nautical_mile / si::hour> {} knot;
// force
// https://en.wikipedia.org/wiki/Poundal
inline constexpr struct poundal : named_unit<"pdl", pound * foot / square(si::second)> {} poundal;
// https://en.wikipedia.org/wiki/Pound_(force)
inline constexpr struct pound_force : named_unit<"lbf", pound * si::standard_gravity> {} pound_force;
// https://en.wikipedia.org/wiki/Kip_(unit),
inline constexpr struct kip : decltype(si::kilo<pound_force>) {} kip;
// pressure
inline constexpr struct psi : named_unit<"psi", pound_force / square(inch)> {} psi;
// power
// https://en.wikipedia.org/wiki/Horsepower#Definitions
inline constexpr struct mechanical_horsepower : named_unit<"hp(I)", mag<33'000> * foot * pound_force / si::minute> {} mechanical_horsepower;
// clang-format on
namespace unit_symbols {
inline constexpr auto lb = pound;
inline constexpr auto oz = ounce;
inline constexpr auto dr = dram;
inline constexpr auto gr = grain;
inline constexpr auto yd = yard;
inline constexpr auto ft = foot;
inline constexpr auto in = inch;
inline constexpr auto P = pica;
inline constexpr auto p = point;
inline constexpr auto mi = mile;
inline constexpr auto le = league;
inline constexpr auto nmi = nautical_mile;
inline constexpr auto kn = knot;
inline constexpr auto kt = knot;
inline constexpr auto mph = mile / si::hour;
inline constexpr auto pdl = poundal;
inline constexpr auto lbf = pound_force;
inline constexpr auto hp = mechanical_horsepower;
} // namespace unit_symbols
} // namespace mp_units::international
-31
View File
@@ -1,31 +0,0 @@
# 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.
cmake_minimum_required(VERSION 3.19)
add_units_module(
isq-iec80000
DEPENDENCIES mp-units::si
HEADERS include/units/isq/iec80000/binary_prefixes.h include/units/isq/iec80000/iec80000.h
include/units/isq/iec80000/modulation_rate.h include/units/isq/iec80000/storage_capacity.h
include/units/isq/iec80000/traffic_intensity.h include/units/isq/iec80000/transfer_rate.h
)
@@ -1,38 +0,0 @@
// 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 <units/prefix.h>
namespace units::isq::iec80000 {
struct kibi : prefix<kibi, "Ki", mag_power<2, 10>()> {};
struct mebi : prefix<mebi, "Mi", mag_power<2, 20>()> {};
struct gibi : prefix<gibi, "Gi", mag_power<2, 30>()> {};
struct tebi : prefix<tebi, "Ti", mag_power<2, 40>()> {};
struct pebi : prefix<pebi, "Pi", mag_power<2, 50>()> {};
struct exbi : prefix<exbi, "Ei", mag_power<2, 60>()> {};
struct zebi : prefix<zebi, "Zi", mag_power<2, 70>()> {};
struct yobi : prefix<yobi, "Yi", mag_power<2, 80>()> {};
} // namespace units::isq::iec80000
@@ -1,158 +0,0 @@
// 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 <units/isq/si/frequency.h>
#include <units/isq/si/prefixes.h>
#include <units/unit.h>
// IWYU pragma: begin_exports
#include <units/quantity.h>
#include <units/reference.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
namespace units::isq::iec80000 {
struct baud : alias_unit<si::hertz, "Bd"> {};
struct kilobaud : prefixed_alias_unit<si::kilohertz, si::kilo, baud> {};
struct megabaud : prefixed_alias_unit<si::megahertz, si::mega, baud> {};
struct gigabaud : prefixed_alias_unit<si::gigahertz, si::giga, baud> {};
struct terabaud : prefixed_alias_unit<si::terahertz, si::tera, baud> {};
struct petabaud : prefixed_alias_unit<si::petahertz, si::peta, baud> {};
struct exabaud : prefixed_alias_unit<si::exahertz, si::exa, baud> {};
struct zettabaud : prefixed_alias_unit<si::zettahertz, si::zetta, baud> {};
struct yottabaud : prefixed_alias_unit<si::yottahertz, si::yotta, baud> {};
// TODO alias_dimension
using dim_modulation_rate = si::dim_frequency;
template<UnitOf<dim_modulation_rate> U, Representation Rep = double>
using modulation_rate = quantity<dim_modulation_rate, U, Rep>;
#ifndef UNITS_NO_LITERALS
inline namespace literals {
constexpr auto operator"" _q_Bd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<baud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_kBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<kilobaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_MBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<megabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_GBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<gigabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_TBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<terabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_PBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<petabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_EBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<exabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_ZBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<zettabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_YBd(unsigned long long l)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(l));
return modulation_rate<yottabaud, std::int64_t>(static_cast<std::int64_t>(l));
}
} // namespace literals
#endif // UNITS_NO_LITERALS
#ifndef UNITS_NO_REFERENCES
namespace modulation_rate_references {
inline constexpr auto Bd = reference<dim_modulation_rate, baud>{};
inline constexpr auto kBd = reference<dim_modulation_rate, kilobaud>{};
inline constexpr auto MBd = reference<dim_modulation_rate, megabaud>{};
inline constexpr auto GBd = reference<dim_modulation_rate, gigabaud>{};
inline constexpr auto TBd = reference<dim_modulation_rate, terabaud>{};
inline constexpr auto PBd = reference<dim_modulation_rate, petabaud>{};
inline constexpr auto EBd = reference<dim_modulation_rate, exabaud>{};
inline constexpr auto ZBd = reference<dim_modulation_rate, zettabaud>{};
inline constexpr auto YBd = reference<dim_modulation_rate, yottabaud>{};
} // namespace modulation_rate_references
namespace references {
using namespace modulation_rate_references;
} // namespace references
#endif // UNITS_NO_REFERENCES
} // namespace units::isq::iec80000
#ifndef UNITS_NO_ALIASES
namespace units::aliases::isq::iec80000::inline modulation_rate {
template<Representation Rep = double>
using Bd = units::isq::iec80000::modulation_rate<units::isq::iec80000::baud, Rep>;
template<Representation Rep = double>
using kBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::kilobaud, Rep>;
template<Representation Rep = double>
using MBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::megabaud, Rep>;
template<Representation Rep = double>
using GBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::gigabaud, Rep>;
template<Representation Rep = double>
using TBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::terabaud, Rep>;
template<Representation Rep = double>
using PBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::petabaud, Rep>;
template<Representation Rep = double>
using EBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::exabaud, Rep>;
template<Representation Rep = double>
using ZBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::zettabaud, Rep>;
template<Representation Rep = double>
using YBd = units::isq::iec80000::modulation_rate<units::isq::iec80000::yottabaud, Rep>;
} // namespace units::aliases::isq::iec80000::inline modulation_rate
#endif // UNITS_NO_ALIASES

Some files were not shown because too many files have changed in this diff Show More