diff --git a/.cmake-format.yaml b/.cmake-format.yaml index 2956b76ee..b507ec9a8 100644 --- a/.cmake-format.yaml +++ b/.cmake-format.yaml @@ -1,3 +1,4 @@ +include: ["cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml"] parse: additional_commands: add_documentation: diff --git a/cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml b/cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml new file mode 100644 index 000000000..cf27cab7f --- /dev/null +++ b/cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml @@ -0,0 +1,61 @@ +parse: + additional_commands: + _jegp_common_yaml_anchors: + kwargs: + PUBLIC_INTERFACE_PRIVATE: &public_interface_private + kwargs: + PUBLIC: + + INTERFACE: + + PRIVATE: + + jegp_add_standardese_sources: + pargs: + nargs: 1 + flags: + - EXCLUDE_PDF_FROM_MAIN + - EXCLUDE_HTML_FROM_MAIN + - EXCLUDE_FROM_ALL + kwargs: + LIBRARIES: + + APPENDICES: + + EXTENSIONS: + + CHECKED: 1 + PDF_PATH: 1 + HTML_PATH: 1 + jegp_add_module: + pargs: &jegp_add_module_pargs + nargs: 1 + flags: + - IMPORTABLE_HEADER + kwargs: + SOURCES: + + COMPILE_OPTIONS: *public_interface_private + LINK_LIBRARIES: *public_interface_private + jegp_cpp_module: + pargs: *jegp_add_module_pargs + jegp_target_link_header_units: + pargs: + nargs: 1+ + jegp_add_headers_test: + pargs: + nargs: 1+ + kwargs: + PRIVATE_REGEXES: + + jegp_add_test: + pargs: + nargs: 1+ + flags: + - COMPILE_ONLY + kwargs: + TYPE: 1 + SOURCES: + + COMPILE_OPTIONS: + + LINK_LIBRARIES: + + jegp_add_build_error: + pargs: + nargs: 1+ + kwargs: + AS: 1 + TYPE: 1 + SOURCE: 1 + COMPILE_OPTIONS: + + LINK_LIBRARIES: + diff --git a/docs/reference/CMakeLists.txt b/docs/reference/CMakeLists.txt new file mode 100644 index 000000000..87d7545d5 --- /dev/null +++ b/docs/reference/CMakeLists.txt @@ -0,0 +1,43 @@ +# 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.0) +project(mp-units-reference-documentations LANGUAGES NONE) + +include(JEGPAddStandardeseSources) + +set(pdf_title "mp-units - A Physical Quantities and Units library for C++") +set(page_license "MIT License") +set(first_library_chapter "nums") +set(last_library_chapter "qties") +# set(pdf_creator "") # Defaults to "the `user.name` of Git's configuration". +# set(document_number [[\unspec]]) +# set(previous_document_number [[\unspec]]) +set(cover_title "mp-units Library Reference Documentations") +set(reply_to "\\href{${PROJECT_HOMEPAGE_URL}/discussions}{Discussions}, \\href{${PROJECT_HOMEPAGE_URL}/issues}{issues}") +jegp_add_standardese_sources( + mp-units-reference-documentations + LIBRARIES intro numbers;quantities + EXTENSIONS macros_extensions + # CHECKED TRUE + PDF_PATH "mp_units.pdf" +) diff --git a/docs/reference/intro.tex b/docs/reference/intro.tex new file mode 100644 index 000000000..1d761ca14 --- /dev/null +++ b/docs/reference/intro.tex @@ -0,0 +1,160 @@ +%!TEX root = std.tex + + +\rSec0[scope]{Scope} + +\pnum +\indextext{scope|(}% +This document describes the contents of the \defn{mp-units library}. +\indextext{scope|)} + + +\rSec0[refs]{References} + +\pnum +\indextext{references|(}% +The following documents are referred to in the text +in such a way that some or all of their content +constitutes requirements of this document. +For dated references, only the edition cited applies. +For undated references, +the latest edition of the referenced document +(including any amendments) applies. +\begin{itemize} +\item +The \Cpp{} Standards Committee. +N4892: \doccite{Working Draft, Standard for Programming Language \Cpp{}}. +Edited by Thomas K\"{o}ppe. +Available from: \url{https://wg21.link/N4892} +\item +The \Cpp{} Standards Committee. +P1841R1: \doccite{Wording for Individually Specializable Numeric Traits}. +Edited by Walter E. Brown. +Available from: \url{https://wg21.link/P1841R1} +\item +The \Cpp{} Standards Committee. +SD-8: \doccite{Standard Library Compatibility}. +Edited by Bryce Lelbach. +Available from: \url{https://wg21.link/SD8} +\item +ISO 80000-2:2019, \doccite{Quantities and units --- Part 2: Mathematics} +\item +IEC 60050 (all parts), +\doccite{International Electrotechnical Vocabulary (IEV)} +\item +IEC 60050-102:2007/AMD3:2021, +\doccite{Amendment 3 --- International Electrotechnical Vocabulary (IEV) --- +Part 102: Mathematics --- General concepts and linear algebra} +\item +IEC 60050-112:2010/AMD2:2020, +\doccite{Amendment 2 --- International Electrotechnical Vocabulary (IEV) --- +Part 112: Quantities and units} +\end{itemize} + +\pnum +N4892 is hereinafter called \defn{\Cpp{}}. + +\pnum +IEC 60050 is hereinafter called \defn{IEV}. +\indextext{references|)} + + +\rSec0[defs]{Terms and definitions} + +\pnum +\indextext{definitions|(}% +For the purposes of this document, +the terms and definitions given in +\Cpp{}, +IEC 60050-102:2007/AMD3:2021, +IEC 60050-112:2010/AMD2:2020, +the terms, definitions, and symbols given in +ISO 80000-2:2019, +and the following apply. + +\pnum +ISO and IEC maintain terminology databases +for use in standardization +at the following addresses: +\begin{itemize} +\item ISO Online browsing platform: available at \url{https://www.iso.org/obp} +\item IEC Electropedia: available at \url{http://www.electropedia.org} +\end{itemize} + +\pnum +Terms that are used only in a small portion of this document +are defined where they are used and italicized where they are defined. + +\indexdefn{modulo}% +\definition{modulo}{def.mod} +operation performed on a set for which a division\irefiev{102-01-21} and an addition\irefiev{102-01-11} are defined, +the result of which, for elements $a$ and $b$ of the set, +is the unique element $r$, if it exists in the set, +such that $a = \lfloor a/b \rfloor b + r$ + + +\rSec0[spec]{Specification} + +\rSec1[spec.ext]{External} + +\pnum +The specification of the mp-units library subsumes +\refcpp{description}, \refcpp{requirements}, \refcpp{concepts.equality}, and SD-8, +all assumingly amended for the context of this library. +\begin{note} +This means that, non exhaustively, +\begin{itemize} +\item \tcode{::mp_units2} is a reserved namespace, and +\item +\tcode{std::vector} +is a program-defined specialization and a library-defined specialization +from the point of view of \Cpp{} and this library, respectively. +\end{itemize} +\end{note} + +\pnum +The mp-units library is not part of the \Cpp{} implementation. + +\rSec1[spec.cats]{Categories} + +\pnum +Detailed specifications for each of the components in the library are in +\ref{\firstlibchapter}--\ref{\lastlibchapter}, +as shown in \tref{lib.cats}. + +\begin{floattable}{Library categories}{lib.cats} +{ll} +\topline +\hdstyle{Clause} & \hdstyle{Category} \\ \capsep +\ref{nums} & Numbers library \\ +\ref{qties} & Quantities library \\ +\end{floattable} + +\pnum +The numbers library\iref{nums} +describes components for dealing with numbers. + +\pnum +The quantities library\iref{qties} +describes components for dealing with quantities. + +\rSec1[headers]{Headers} + +\pnum +The mp-units library provides the +\defnx{mp-units library headers}{header!mp-units library}, +shown in \tref{headers.mp.units}. + +\begin{multicolfloattable}{mp-units library headers}{headers.mp.units} +{l} +\tcode{} \\ +\tcode{} \\ +\end{multicolfloattable} + +\rSec1[spec.reqs]{Library-wide requirements} + +\rSec2[spec.res.names]{Reserved names} + +\pnum +The mp-units library reserves macro names that start with +\tcode{MP_UNITS\opt{\gterm{digit-sequence}}_}. diff --git a/docs/reference/macros_extensions.tex b/docs/reference/macros_extensions.tex new file mode 100644 index 000000000..1599f7c54 --- /dev/null +++ b/docs/reference/macros_extensions.tex @@ -0,0 +1,20 @@ +% \newcommand{\idxmdl}[1]{#1@\CodeStylex{#1}} +\newcommand{\idxexposid}[1]{#1@\ExposStylex{#1}} + +\newcommand{\indexlibraryspec}[2]{\indexlibrarymisc{#1}{\idxcode{#2}}} + +\newcommand{\ExposStylex}[1]{\CodeStylex{\textit{#1}}} + +\newnoteenvironment{source}{Source}{end source} + +\newcommand{\dvalue}{\Fundesc{Default value}} + +%% Inline non-parenthesized C++ reference +\newcommand{\refcpp}[1]{\href{https://wg21.link/#1}{\Cpp{} [#1]}} +\newcommand{\irefcpp}[1]{\nolinebreak[3] (\refcpp{#1})} +\newcommand{\refcppx}[2]{\href{https://wg21.link/#1\##2}{\Cpp{} [#1]}} +\newcommand{\irefcppx}[2]{\nolinebreak[3] (\refcppx{#1}{#2})} + +%% Inline IEV reference +\newcommand{\refiev}[1]{\href{https://www.electropedia.org/iev/iev.nsf/display?openform&ievref=#1}{IEV #1}} +\newcommand{\irefiev}[1]{\nolinebreak[3] (\refiev{#1})} diff --git a/docs/reference/numbers.tex b/docs/reference/numbers.tex new file mode 100644 index 000000000..17c8eb28d --- /dev/null +++ b/docs/reference/numbers.tex @@ -0,0 +1,607 @@ +%!TEX root = std.tex +\Sec0[nums]{Numbers library} + +\Sec1[nums.summary]{Summary} + +\pnum +This Clause describes components for dealing with numbers, +as summarized in \tref{nums.summary}. + +\begin{libsumtab}{Numbers library summary}{nums.summary} +\ref{num.traits} & Traits & \tcode{} \\ +\ref{num.concepts} & Concepts & \\ +\end{libsumtab} + +\Sec1[nums.syn]{Header \tcode{} synopsis} + +\indexheader{mp-units/numbers.h}% +\begin{codeblock} +namespace mp_units { + +// \ref{num.traits}, traits + +// \ref{num.opt.ins}, \cname{number} opt-ins +template +struct enable_number; +template +struct enable_complex_number; + +template +constexpr bool @\libglobal{enable_number_v}@ = enable_number::value; +template +constexpr bool @\libglobal{enable_complex_number_v}@ = enable_complex_number::value; + +// \ref{num.ids}, identities +template +struct number_zero; +template +struct number_one; + +template +constexpr T @\libglobal{number_zero_v}@ = number_zero::value; +template +constexpr T @\libglobal{number_one_v}@ = number_one::value; + +// \ref{num.assoc.types}, associated types +template +struct vector_scalar; + +template +using number_difference_t = decltype(std::declval() - std::declval()); +template +using @\libglobal{vector_scalar_t}@ = vector_scalar::type; + +// \ref{num.concepts}, concepts +template +concept number = @\seebelow@; +template +concept common_number_with = @\seebelow@; +template +concept ordered_number = @\seebelow@; +template +concept number_line = @\seebelow@; +template +concept modulo_with = @\seebelow@; +template +concept compound_modulo_with = @\seebelow@; +template +concept modulus_for = @\seebelow@; +template +concept compound_modulus_for = @\seebelow@; +template +concept negative = @\seebelow@; +template +concept set_with_inverse = @\seebelow@; +template +concept point_space_for = @\seebelow@; +template +concept compound_point_space_for = @\seebelow@; +template +concept point_space = @\seebelow@; +template +concept vector_space_for = @\seebelow@; +template +concept compound_vector_space_for = @\seebelow@; +template +concept scalar_for = @\seebelow@; +template +concept field_for = @\seebelow@; +template +concept compound_scalar_for = @\seebelow@; +template +concept compound_field_for = @\seebelow@; +template +concept vector_space = @\seebelow@; +template +concept f_vector_space = @\seebelow@; +template +concept field_number = @\seebelow@; +template +concept field_number_line = @\seebelow@; +template +concept scalar_number = @\seebelow@; + +} // namespace mp_units +\end{codeblock} + +\Sec1[num.traits]{Traits} + +\Sec2[num.traits.reqs]{Requirements} + +\pnum +Subclause \ref{num.traits} subsumes \refcpp{meta.rqmts}, +assumingly amended for its context. +Pursuant to the subsumed \refcpp{namespace.std}\iref{spec.ext}, +each class template specified in \ref{num.traits} +may be specialized for any numeric type\irefcppx{numeric.requirements}{def:type,numeric}. + +\pnum +\indexlibrary{\idxexposid{number-trait}!{\tcode{const T}}}% +Each template \tcode{\placeholder{number-trait}} specified in \ref{num.traits} +has a partial specialization of the form +\begin{codeblock} +template +struct @\placeholder{number-trait}@ : @\placeholder{number-trait}@ { }; +\end{codeblock} + +\pnum +Each template \tcode{\placeholder{number-trait}} specified in \ref{num.opt.ins} +is a \oldconcept{UnaryTypeTrait} +with a base characteristic of \tcode{std::bool_constant<\placeholder{B}>}. +\tcode{\placeholder{B}} is a value consistent with \placeholdernc{\tcode{number-trait}}'s specification. + +\pnum +Each template specified in \ref{num.ids} +is a numeric distinguished value trait +(\href{https://wg21.link/P1841R3}{P1841R3} [num.traits.val]), +except that there are no declared specializations for arithmetic or volatile-qualified types. + +\pnum +A specialization of any template \tcode{\placeholder{number-trait}} specified in \ref{num.assoc.types} +has no members other than a \tcode{type} member +that names a type consistent with \placeholdernc{\tcode{number-trait}}'s specification. + +\Sec2[num.opt.ins]{\cname{number} opt-ins} + +\begin{itemdecl} +template +struct @\libglobal{enable_number}@ : @\seebelow@ {}; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\cvalue +\tcode{true} if \tcode{T} represents a number, and +\tcode{false} otherwise. + +\pnum +\dvalue +\tcode{true} if \tcode{vector_scalar_t} is valid, and +\tcode{false} otherwise. +\end{itemdescr} + +\indexlibraryspec{enable_number}{std::chrono::time_point}% +\indexlibraryspec{enable_number}{std::chrono::day}% +\indexlibraryspec{enable_number}{std::chrono::month}% +\indexlibraryspec{enable_number}{std::chrono::year}% +\indexlibraryspec{enable_number}{std::chrono::weekday}% +\indexlibraryspec{enable_number}{std::chrono::year_month}% +\begin{itemdecl} +template +struct enable_number> : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +\end{itemdecl} + +\begin{itemdecl} +template +struct @\libglobal{enable_complex_number}@ : std::false_type {}; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\cvalue +\tcode{true} if \tcode{T} represents a complex number\irefiev{102-02-09}, and +\tcode{false} otherwise. +\end{itemdescr} + +\indexlibraryspec{enable_complex_number}{std::complex}% +\begin{itemdecl} +template +struct enable_complex_number> : std::true_type {}; +\end{itemdecl} + +\Sec2[num.ids]{Identities} + +\begin{codeblock} +template +concept @\defexposconcept{inferable-identities}@ = + common_number_with> && std::constructible_from; +\end{codeblock} + +\indexlibrary{\idxcode{number_zero}!\idxexposconcept{inferable-identities}}% +\begin{itemdecl} +template +struct @\libglobal{number_zero}@ { @\seebelow@ }; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\cvalue +\tcode{T}'s neutral element for addition\irefiev{102-01-12}, if any. + +\pnum +\dvalue +If \tcode{T} models \exposconcept{inferable-identities}, \tcode{T(0)}. +\end{itemdescr} + +\indexlibrary{\idxcode{number_one}!\idxexposconcept{inferable-identities}}% +\begin{itemdecl} +template +struct @\libglobal{number_one}@ { @\seebelow@ }; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\cvalue +\tcode{T}'s neutral element for multiplication\irefiev{102-01-19}, if any. + +\pnum +\dvalue +If \tcode{T} models \exposconcept{inferable-identities}, \tcode{T(1)}. +\end{itemdescr} + +\Sec2[num.assoc.types]{Associated types} + +\begin{itemdecl} +template +using @\libglobal{number_difference_t}@ = decltype(std::declval() - std::declval()); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\tcode{number_difference_t} represents \tcode{T}'s difference's\irefiev{102-01-17} type, if any. +\end{itemdescr} + +\indexlibrary{\idxcode{vector_scalar}!arithmetic type}% +\indexlibraryspec{vector_scalar}{std::complex}% +\indexlibraryspec{vector_scalar}{std::chrono::duration}% +\begin{itemdecl} +template +struct @\libglobal{vector_scalar}@ {}; +template<@\seebelow@> +struct vector_scalar<@\seebelow@> { + using type = @\seebelow@; +}; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ctype +Scalar\irefiev{102-02-18} for the vector space\irefiev{102-03-01} \tcode{T}. + +\pnum +Each row of \tref{num.scalar} denotes a specialization. + +\begin{floattable}{Specializations of \tcode{vector_scalar}}{num.scalar}{lll} +\topline +\lhdr{\fakegrammarterm{template-parameter-list}} + & \chdr{\fakegrammarterm{template-argument-list}} + & \rhdr{\Fundescx{Type}} \\ \rowsep +\tcode{std::integral T} & \tcode{T} & \tcode{T} \\ \rowsep +\tcode{std::floating_point T} & \tcode{T} & \tcode{T} \\ \rowsep +\tcode{class T} & \tcode{std::complex} & \tcode{T} \\ \rowsep +\tcode{class T, class U} & \tcode{std::chrono::duration} & \tcode{T} \\ +\end{floattable} +\end{itemdescr} + +\Sec1[num.concepts]{Concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{number}@ = enable_number_v && std::regular; + +template +concept @\deflibconcept{common_number_with}@ = + @\libconcept{number}@ && @\libconcept{number}@ && std::common_with && @\libconcept{number}@>; + +template +concept @\deflibconcept{ordered_number}@ = @\libconcept{number}@ && std::totally_ordered; +\end{itemdecl} + +\begin{itemdecl} +template +concept @\deflibconcept{number_line}@ = + @\libconcept{ordered_number}@ && + requires(T& v) { + number_one_v>; + { ++v } -> std::same_as; + { --v } -> std::same_as; + { v++ } -> std::same_as; + { v-- } -> std::same_as; + }; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{v} and \tcode{u} be equal objects of type \tcode{T}, +and \tcode{one} be the value \tcode{number_one_v>}. +\tcode{T} models \libconcept{number_line} only if +\begin{itemize} +\item +The expressions \tcode{++v} and \tcode{v++} have the same domain\irefcppx{concepts.equality}{def:expression,domain}. +\item +The expressions \tcode{--v} and \tcode{v--} have the same domain. +\item +If \tcode{v} is incrementable\irefcppx{iterator.concept.winc}{\#def:incrementable}, +then both \tcode{++v} and \tcode{v++} add \tcode{one} to \tcode{v}, +and the following expressions all equal \tcode{true}: +\begin{itemize} +\item \tcode{v++ == u}, +\item \tcode{((void)v++, v) == ++u}, and +\item \tcode{std::addressof(++v) == std::addressof(v)}. +\end{itemize} +\item +If \tcode{v} is decrementable\irefcppx{range.iota.view}{\#def:decrementable}, +then both \tcode{--v} and \tcode{v--} subtract \tcode{one} from \tcode{v}, +and the following expressions all equal \tcode{true}: +\begin{itemize} +\item \tcode{v-- == u}, +\item \tcode{((void)v--, v) == --u}, and +\item \tcode{std::addressof(--v) == std::addressof(v)}. +\end{itemize} +\end{itemize} +\end{itemdescr} + +\begin{itemdecl} +template concept @\defexposconceptnc{addition-with}@ = + @\libconcept{number}@ && + @\libconcept{number}@ && + requires(const T& c, const U& d) { + { c + d } -> common_number_with; + { d + c } -> common_number_with; + }; + +template concept @\defexposconceptnc{compound-addition-with}@ = + @\exposconcept{addition-with}@ && + requires(T& l, const U& d) { + { l += d } -> std::same_as; + }; + +template concept @\defexposconceptnc{subtraction-with}@ = + @\exposconcept{addition-with}@ && + requires(const T& c, const U& d) { + { c - d } -> common_number_with; + }; + +template concept @\defexposconceptnc{compound-subtraction-with}@ = + @\exposconcept{subtraction-with}@ && + @\exposconcept{compound-addition-with}@ && + requires(T& l, const U& d) { + { l -= d } -> std::same_as; + }; + +template concept @\defexposconceptnc{multiplication-with}@ = + @\libconcept{number}@ && + @\libconcept{number}@ && + @\libconcept{number}@ && + requires(const T& c, const U& u) { + { c * u } -> common_number_with; + }; + +template concept @\defexposconceptnc{compound-multiplication-with}@ = + @\exposconcept{multiplication-with}@ && + requires(T& l, const U& u) { + { l *= u } -> std::same_as; + }; + +template concept @\defexposconceptnc{division-with}@ = + @\exposconcept{multiplication-with}@ && + @\exposconcept{multiplication-with}@ && + requires(const T& c, const U& u) { + { c / u } -> common_number_with; + }; + +template concept @\defexposconceptnc{compound-division-with}@ = + @\exposconcept{division-with}@ && + @\exposconcept{compound-multiplication-with}@ && + requires(T& l, const U& u) { + { l /= u } -> std::same_as; + }; + +template concept @\deflibconcept{modulo_with}@ = + @\libconcept{number}@ && + @\libconcept{number}@ && + requires(const T& c, const U& u) { + { c % u } -> common_number_with; + }; + +template concept @\deflibconcept{compound_modulo_with}@ = + @\libconcept{modulo_with}@ && + requires(T& l, const U& u) { + { l %= u } -> std::same_as; + }; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{q} be an object of type \tcode{T}, +and \tcode{r} be an object of type \tcode{U}. + +\pnum +For \exposconcept{addition-with} and \exposconcept{compound-addition-with}, +let $E$ be the expression \tcode{q + r} or \tcode{r + q}, and \tcode{q += r}, respectively, and +let $F$ be the addition\irefiev{102-01-11} of the inputs to $E$. + +\pnum +For \exposconcept{subtraction-with} and \exposconcept{compound-subtraction-with}, +let $E$ be the expression \tcode{q - r} and \tcode{q -= r}, respectively, and +let $F$ be the subtraction\irefiev{102-01-13} of the inputs to $E$. + +\pnum +For \exposconcept{multiplication-with} and \exposconcept{compound-multiplication-with}, +let $E$ be the expression \tcode{q * r} and \tcode{q *= r}, respectively, and +let $F$ be the multiplication\irefiev{102-01-18} of the inputs to $E$. + +\pnum +For \exposconcept{division-with} and \exposconcept{compound-division-with}, +let $E$ be the expression \tcode{q / r} and \tcode{q /= r}, respectively, and +let $F$ be the division\irefiev{102-01-21} of the inputs to $E$. + +\pnum +For \libconcept{modulo_with} and \libconcept{compound_modulo_with}, +let $E$ be the expression \tcode{q \% r} and \tcode{q \%= r}, respectively, and +let $F$ be the modulo\iref{def.mod} of the inputs to $E$. + +\pnum +\tcode{T} respectively models +\tcode{\exposconceptnc{addition-with}}, +\tcode{\exposconceptnc{compound-addition-with}}, +\tcode{\exposconceptnc{subtraction-with}}, +\tcode{\exposconceptx{com\-pound-subtraction-with}{compound-subtraction-with}}, +\tcode{\exposconceptnc{multiplication-with}}, +\tcode{\exposconceptnc{compound-multiplication-with}}, +\tcode{\exposconceptnc{division-with}}, +\tcode{\exposconceptnc{compound-division-with}}, +\tcode{\libconcept{modulo_with}}, and +\tcode{\libconcept{compound_modulo_with}} +only if, for each respective $E$, when the inputs to $E$ are in the domain of $E$ +\begin{itemize} +\item +$E$ performs $F$ in an unspecified set. +\item +If the operator of $E$ is an \fakegrammarterm{assignment-operator}, +\begin{itemize} +\item +$E$ maps the value of $F$ to \tcode{q}, and +the result of $E$ is a reference to \tcode{q}, and +\item +the result of $E$ is the value of $F$ mapped to the type of $E$ otherwise. +\end{itemize} +\end{itemize} +\end{itemdescr} + +\begin{itemdecl} +template +concept @\deflibconcept{modulus_for}@ = @\libconcept{modulo_with}@; +template +concept @\deflibconcept{compound_modulus_for}@ = @\libconcept{compound_modulo_with}@; +\end{itemdecl} + +\begin{itemdecl} +template concept @\deflibconcept{negative}@ = + @\exposconcept{compound-addition-with}@ && + requires(const T& c) { + number_zero_v; + { -c } -> common_number_with; + }; + +template concept @\deflibconcept{set_with_inverse}@ = + @\exposconcept{compound-multiplication-with}@ && + requires(const T& c) { + { number_one_v / c } -> std::common_with; + }; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{v} be an object of type \tcode{T}. + +\pnum +For \libconcept{negative}, +let $E$ be the expression \tcode{-v}, and +let $F$ be the negative\irefiev{102-01-14} of \tcode{v}. + +\pnum +For \libconcept{set_with_inverse}, +let $E$ be the expression \tcode{number_one_v / v}, and +let $F$ be the inverse\irefiev{102-01-24} of \tcode{v}. + +\pnum +\tcode{T} respectively models +\tcode{\libconcept{negative}} and +\tcode{\libconcept{set_with_inverse}} +only if, for the respective $E$, when \tcode{v} is in the domain of $E$ +\begin{itemize} +\item +$E$ performs $F$ in an unspecified set. +\item +The result of $E$ is the value of $F$ mapped to the type of $E$. +\end{itemize} +\end{itemdescr} + +\begin{itemdecl} +template +concept @\deflibconcept{point_space_for}@ = + @\exposconcept{subtraction-with}@ && @\libconcept{negative}@ && common_number_with, U>; +template +concept @\deflibconcept{compound_point_space_for}@ = @\libconcept{point_space_for}@ && @\exposconcept{compound-subtraction-with}@; +template +concept @\deflibconcept{point_space}@ = @\libconcept{compound_point_space_for}@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\begin{note} +The \libconcept{point_space} concept is modeled by types +that behave similarly to \tcode{std::chrono::sys_seconds}. +\end{note} +\end{itemdescr} + +\begin{itemdecl} +template +concept @\deflibconcept{vector_space_for}@ = @\libconcept{point_space_for}@; +template +concept @\deflibconcept{compound_vector_space_for}@ = @\libconcept{compound_point_space_for}@; + +template +concept @\defexposconceptnc{weak-scalar}@ = + common_number_with> && @\libconcept{point_space}@ && @\libconcept{negative}@; + +template +concept @\defexposconceptnc{scales-with}@ = common_number_with> && @\exposconcept{weak-scalar}@ && + @\exposconcept{multiplication-with}@ && @\libconcept{set_with_inverse}@; +template +concept @\defexposconceptnc{compound-scales-with}@ = @\exposconcept{scales-with}@ && @\exposconcept{compound-multiplication-with}@; + +template +concept @\deflibconcept{scalar_for}@ = @\exposconcept{scales-with}@; +template +concept @\deflibconcept{field_for}@ = @\libconcept{scalar_for}@ && @\exposconcept{division-with}@; +template +concept @\deflibconcept{compound_scalar_for}@ = @\exposconcept{compound-scales-with}@; +template +concept @\deflibconcept{compound_field_for}@ = @\libconcept{compound_scalar_for}@ && @\exposconcept{compound-division-with}@; + +template +concept @\deflibconcept{vector_space}@ = @\libconcept{point_space}@ && @\exposconcept{compound-scales-with}@>; +template +concept @\deflibconcept{f_vector_space}@ = @\libconcept{vector_space}@ && @\exposconcept{compound-division-with}@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\begin{note} +The \libconcept{f_vector_space} concept is modeled by types +that behave similarly to \tcode{std::chrono::seconds}. +\end{note} +\end{itemdescr} + +\begin{itemdecl} +template +concept @\deflibconcept{field_number}@ = @\libconcept{f_vector_space}@ && @\exposconcept{compound-scales-with}@; + +template +concept @\deflibconcept{field_number_line}@ = @\libconcept{field_number}@ && @\libconcept{number_line}@; + +template +concept @\deflibconcept{scalar_number}@ = @\libconcept{field_number}@ && (@\libconcept{field_number_line}@ || enable_complex_number_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\begin{note} +The \libconcept{field_number} concept is modeled by types +that behave similarly to \tcode{std::complex}. +It represents an approximation of a field, see \refiev{102-02-18}, Note 2 to entry. +\end{note} + +\pnum +\begin{note} +The \libconcept{field_number_line} concept is modeled by types +that behave similarly to \tcode{double}. +\end{note} + +\pnum +\begin{note} +\libconcept{scalar_number} represents an approximation of a scalar number\irefiev{102-02-18}. +\end{note} +\end{itemdescr} diff --git a/docs/reference/quantities.tex b/docs/reference/quantities.tex new file mode 100644 index 000000000..30f53f131 --- /dev/null +++ b/docs/reference/quantities.tex @@ -0,0 +1,46 @@ +%!TEX root = std.tex +\Sec0[qties]{Quantities library} + +\Sec1[qties.summary]{Summary} + +\pnum +This Clause describes components for dealing with quantities, +as summarized in \tref{qties.summary}. + +\begin{modularlibsumtab}{Quantities library summary}{qties.summary} +\ref{qty.concepts} & Concepts & \tcode{} \\ +\end{modularlibsumtab} + +\Sec1[qty.syn]{Header \tcode{} synopsis} + +\indexheader{mp-units/quantity.h}% +\indexlibraryspec{number_scalar}{quantity}% +\begin{codeblock} +#include // see \ref{nums.syn} + +namespace mp_units { + +// \ref{qty.concepts}, concepts +template +concept scalar_quantity = @\seebelow@; + +template +struct number_scalar : number_scalar {}; + +} // namespace mp_units +\end{codeblock} + +\Sec1[qty.concepts]{Concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{scalar_quantity}@ = Quantity && @\libconcept{scalar_number}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\begin{note} +The \libconcept{scalar_quantity} concept +represents an approximation of a scalar quantity\irefiev{102-02-19}. +\end{note} +\end{itemdescr} diff --git a/example/include/ranged_representation.h b/example/include/ranged_representation.h index 82f518468..f22e83bcc 100644 --- a/example/include/ranged_representation.h +++ b/example/include/ranged_representation.h @@ -38,27 +38,41 @@ template) auto Max> using is_in_range_t = decltype(is_in_range); -template) auto Min, +template) auto Min, MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to) auto Max> class ranged_representation : public validated_type> { public: using validated_type>::validated_type; constexpr ranged_representation() : validated_type>(T{}) {} - [[nodiscard]] constexpr ranged_representation operator-() const - requires requires(T t) { -t; } + [[nodiscard]] constexpr ranged_representation operator-() const { return ranged_representation(-this->value()); } + + constexpr ranged_representation& operator+=(const T& that) { - return ranged_representation(-this->value()); + return *this = ranged_representation(this->value() + that); + } + constexpr ranged_representation& operator-=(const T& that) + { + return *this = ranged_representation(this->value() - that); + } + constexpr ranged_representation& operator*=(const T& rhs) + { + return *this = ranged_representation(this->value() * rhs); } }; template inline constexpr bool mp_units::is_scalar> = mp_units::is_scalar; +template +struct mp_units::vector_scalar> : std::type_identity {}; + template inline constexpr bool mp_units::treat_as_floating_point> = mp_units::treat_as_floating_point; +static_assert(mp_units::vector_space>); + template struct MP_UNITS_STD_FMT::formatter> : formatter { template diff --git a/example/measurement.cpp b/example/measurement.cpp index b67416273..82f4a432a 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -30,7 +30,7 @@ namespace { -template +template class measurement { public: using value_type = T; @@ -65,6 +65,18 @@ class measurement { return measurement(lhs.value() - rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty())); } + constexpr measurement& operator+=(const measurement& that) + { + value_ = *this + that; + return *this; + } + + constexpr measurement& operator-=(const measurement& that) + { + value_ = *this - that; + return *this; + } + [[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) { const auto val = lhs.value() * rhs.value(); @@ -78,6 +90,12 @@ class measurement { return measurement(val, val * lhs.relative_uncertainty()); } + constexpr measurement& operator*=(const value_type& value) + { + value_ = *this * value; + return *this; + } + [[nodiscard]] friend constexpr measurement operator*(const value_type& value, const measurement& rhs) { const auto val = rhs.value() * value; @@ -125,7 +143,11 @@ inline constexpr bool mp_units::is_scalar> = true; template inline constexpr bool mp_units::is_vector> = true; +template +struct mp_units::vector_scalar> : std::type_identity {}; + static_assert(mp_units::RepresentationOf, mp_units::quantity_character::scalar>); +static_assert(mp_units::vector_space>); namespace { diff --git a/src/core/include/mp-units/bits/external/type_list.h b/src/core/include/mp-units/bits/external/type_list.h index 5369ce18d..80b66e060 100644 --- a/src/core/include/mp-units/bits/external/type_list.h +++ b/src/core/include/mp-units/bits/external/type_list.h @@ -24,6 +24,7 @@ #include // IWYU pragma: keep #include +#include #include MP_UNITS_DIAGNOSTIC_PUSH diff --git a/src/core/include/mp-units/bits/fwd.h b/src/core/include/mp-units/bits/fwd.h new file mode 100644 index 000000000..22ac3247a --- /dev/null +++ b/src/core/include/mp-units/bits/fwd.h @@ -0,0 +1,34 @@ +// 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 +#include + +namespace mp_units { + +template Rep> + requires vector_space +class quantity; + +} // namespace mp_units diff --git a/src/core/include/mp-units/bits/quantity_concepts.h b/src/core/include/mp-units/bits/quantity_concepts.h index 437d64e30..f9bdaeb0b 100644 --- a/src/core/include/mp-units/bits/quantity_concepts.h +++ b/src/core/include/mp-units/bits/quantity_concepts.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -29,9 +30,6 @@ namespace mp_units { -template Rep> -class quantity; - #if defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 17 template #else diff --git a/src/core/include/mp-units/bits/quantity_point_concepts.h b/src/core/include/mp-units/bits/quantity_point_concepts.h index 90a9e85be..f0cebea18 100644 --- a/src/core/include/mp-units/bits/quantity_point_concepts.h +++ b/src/core/include/mp-units/bits/quantity_point_concepts.h @@ -107,6 +107,7 @@ concept PointOriginFor = PointOrigin && QuantitySpecOf auto PO, RepresentationOf Rep> + requires vector_space class quantity_point; #if defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 17 diff --git a/src/core/include/mp-units/customization_points.h b/src/core/include/mp-units/customization_points.h index e4c169169..0ddd4eea7 100644 --- a/src/core/include/mp-units/customization_points.h +++ b/src/core/include/mp-units/customization_points.h @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/src/core/include/mp-units/numbers.h b/src/core/include/mp-units/numbers.h new file mode 100644 index 000000000..1c48797f4 --- /dev/null +++ b/src/core/include/mp-units/numbers.h @@ -0,0 +1,299 @@ +// 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 +#include +#include +#include +#include + +namespace mp_units { + +template +struct vector_scalar; + +template +using number_difference_t = decltype(std::declval() - std::declval()); +template +using vector_scalar_t = vector_scalar::type; + +namespace detail { + +template +struct inferred_number_zero {}; +template +struct inferred_number_one {}; + +template +concept is_inferred_number = std::regular>; + +} // namespace detail + +template +struct enable_number : std::bool_constant> {}; +template +struct enable_complex_number : std::false_type {}; +template +struct number_zero : detail::inferred_number_zero {}; +template +struct number_one : detail::inferred_number_one {}; +template +struct vector_scalar {}; + +template +constexpr bool enable_number_v = enable_number::value; +template +constexpr bool enable_complex_number_v = enable_complex_number::value; + +template +constexpr T number_zero_v = number_zero::value; +template +constexpr T number_one_v = number_one::value; + +template +concept number = enable_number_v && std::regular; + +template +concept common_number_with = number && number && std::common_with && number>; + +template +concept ordered_number = number && std::totally_ordered; + +template +concept number_line = // clang-format off + ordered_number && + requires(T& v) { + number_one_v>; + { ++v } -> std::same_as; + { --v } -> std::same_as; + { v-- } -> std::same_as; + { v-- } -> std::same_as; + }; // clang-format on + +namespace detail { + +template +concept addition_with = // clang-format off + number && + number && + requires( const T & c, const U & d) { + { c + d } -> common_number_with; + { d + c } -> common_number_with; + }; // clang-format on + +template +concept compound_addition_with = // clang-format off + addition_with && + requires(T& l, const U & d) { + { l += d } -> std::same_as; + }; // clang-format on + +template +concept subtraction_with = // clang-format off + addition_with && + requires( const T & c, const U & d) { + { c - d } -> common_number_with; + }; // clang-format on + +template +concept compound_subtraction_with = // clang-format off + subtraction_with && + compound_addition_with && + requires(T& l, const U & d) { + { l -= d } -> std::same_as; + }; // clang-format on + +template +concept multiplication_with = // clang-format off + number && + number && + number && + requires( const T & c, const U & u) { + { (c * u) } -> common_number_with; + }; // clang-format on + +template +concept compound_multiplication_with = // clang-format off + multiplication_with && + requires(T& l, const U & u) { + { l *= u } -> std::same_as; + }; // clang-format on + +template +concept division_with = // clang-format off + multiplication_with && + multiplication_with && + requires( const T & c, const U & u) { + { c / u } -> common_number_with; + }; // clang-format on + +template +concept compound_division_with = // clang-format off + division_with && + compound_multiplication_with && + requires(T& l, const U & u) { + { l /= u } -> std::same_as; + }; // clang-format on + +} // namespace detail + +template +concept modulo_with = // clang-format off + number && + number && + requires( const T & c, const U & u) { + { (c % u) } -> common_number_with; + }; // clang-format on + +template +concept compound_modulo_with = // clang-format off + modulo_with && + requires(T& l, const U & u) { + { l %= u } -> std::same_as; + }; // clang-format on + +template +concept modulus_for = modulo_with; +template +concept compound_modulus_for = compound_modulo_with; + +template +concept negative = // clang-format off + detail::compound_addition_with && + requires( const T & c) { + number_zero_v; + { -c } -> common_number_with; + }; // clang-format on + +template +concept set_with_inverse = // clang-format off + detail::compound_multiplication_with && + requires( const T & c) { + { number_one_v / c } -> std::common_with; + }; // clang-format on + +template +concept point_space_for = + detail::subtraction_with && negative && common_number_with, U>; +template +concept compound_point_space_for = point_space_for && detail::compound_subtraction_with; +template +concept point_space = compound_point_space_for>; + +template +concept vector_space_for = point_space_for; +template +concept compound_vector_space_for = compound_point_space_for; + +namespace detail { + +template +concept weak_scalar = common_number_with> && point_space && negative; + +template +concept scales_with = + common_number_with> && weak_scalar && multiplication_with && set_with_inverse; +template +concept compound_scales_with = scales_with && detail::compound_multiplication_with; + +} // namespace detail + +template +concept scalar_for = detail::scales_with; +template +concept field_for = scalar_for && detail::division_with; +template +concept compound_scalar_for = detail::compound_scales_with; +template +concept compound_field_for = compound_scalar_for && detail::compound_division_with; + +template +concept vector_space = point_space && detail::compound_scales_with>; +template +concept f_vector_space = vector_space && detail::compound_division_with>; + +template +concept field_number = f_vector_space && detail::compound_scales_with; + +template +concept field_number_line = field_number && number_line; + +template +concept scalar_number = field_number && (field_number_line || enable_complex_number_v); + +template +struct enable_number : enable_number {}; +template +struct enable_number> : std::true_type {}; +// #if __cpp_lib_chrono >= 201803 +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +template<> +struct enable_number : std::true_type {}; +// #endif + +template +struct enable_complex_number : enable_complex_number {}; +template +struct enable_complex_number> : std::true_type {}; + +namespace detail { + +template +concept inferable_identities = common_number_with> && std::constructible_from; + +template +struct inferred_number_zero { + static constexpr auto value = T(0); +}; +template +struct inferred_number_one { + static constexpr auto value = T(1); +}; + +} // namespace detail + +template +struct number_zero : number_zero {}; +template +struct number_one : number_one {}; + +template +struct vector_scalar : vector_scalar {}; +template +struct vector_scalar : std::type_identity {}; +template +struct vector_scalar : std::type_identity {}; +template +struct vector_scalar> : std::type_identity {}; +template +struct vector_scalar> : std::type_identity {}; + +} // namespace mp_units diff --git a/src/core/include/mp-units/quantity.h b/src/core/include/mp-units/quantity.h index e1e989cd5..0072cb7ae 100644 --- a/src/core/include/mp-units/quantity.h +++ b/src/core/include/mp-units/quantity.h @@ -83,6 +83,7 @@ using common_quantity_for = quantity Rep = double> + requires vector_space class quantity { public: Rep numerical_value_; // needs to be public for a structural type @@ -201,92 +202,60 @@ class quantity { } [[nodiscard]] constexpr Quantity auto operator-() const - requires requires(rep v) { - { - -v - } -> std::common_with; - } + requires negative { return make_quantity(-numerical_value_); } template - friend constexpr decltype(auto) operator++(Q&& q) - requires std::derived_from, quantity> && requires(rep v) { - { - ++v - } -> std::same_as; - } + friend constexpr decltype(auto) operator++(Q && q) + requires std::derived_from, quantity> && number_line { ++q.numerical_value_; return std::forward(q); } [[nodiscard]] constexpr Quantity auto operator++(int) - requires requires(rep v) { - { - v++ - } -> std::common_with; - } + requires number_line { return make_quantity(numerical_value_++); } template - friend constexpr decltype(auto) operator--(Q&& q) - requires std::derived_from, quantity> && requires(rep v) { - { - --v - } -> std::same_as; - } + friend constexpr decltype(auto) operator--(Q && q) + requires std::derived_from, quantity> && number_line { --q.numerical_value_; return std::forward(q); } [[nodiscard]] constexpr Quantity auto operator--(int) - requires requires(rep v) { - { - v-- - } -> std::common_with; - } + requires number_line { return make_quantity(numerical_value_--); } // compound assignment operators template - requires std::derived_from, quantity> && requires(rep a, rep b) { - { - a += b - } -> std::same_as; - } - friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity& rhs) + requires std::derived_from, quantity> + friend constexpr decltype(auto) operator+=(Q && lhs, const quantity & rhs) { lhs.numerical_value_ += rhs.numerical_value_; return std::forward(lhs); } template - requires std::derived_from, quantity> && requires(rep a, rep b) { - { - a -= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity& rhs) + requires std::derived_from, quantity> + friend constexpr decltype(auto) operator-=(Q && lhs, const quantity & rhs) { lhs.numerical_value_ -= rhs.numerical_value_; return std::forward(lhs); } template - requires std::derived_from, quantity> && (!treat_as_floating_point) && - requires(rep a, rep b) { - { - a %= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs) + requires std::derived_from, quantity> && + (!treat_as_floating_point) && compound_modulus_for + friend constexpr decltype(auto) operator%=(Q && lhs, const quantity & rhs) { gsl_ExpectsAudit(rhs != zero()); @@ -294,40 +263,26 @@ class quantity { return std::forward(lhs); } - template - requires std::derived_from, quantity> && (!Quantity) && - requires(rep a, const Value b) { - { - a *= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& v) + template Value> + requires std::derived_from, quantity> + friend constexpr decltype(auto) operator*=(Q && lhs, const Value & v) { lhs.numerical_value_ *= v; return std::forward(lhs); } template Q2> - requires std::derived_from, quantity> && (Q2::unit == ::mp_units::one) && - requires(rep a, const typename Q2::rep b) { - { - a *= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator*=(Q1&& lhs, const Q2& rhs) + requires std::derived_from, quantity> && + (Q2::unit == ::mp_units::one) && compound_scalar_for + friend constexpr decltype(auto) operator*=(Q1 && lhs, const Q2 & rhs) { lhs.numerical_value_ *= rhs.numerical_value_; return std::forward(lhs); } - template - requires std::derived_from, quantity> && (!Quantity) && - requires(rep a, const Value b) { - { - a /= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v) + template Value> + requires std::derived_from, quantity> + friend constexpr decltype(auto) operator/=(Q && lhs, const Value & v) { gsl_ExpectsAudit(v != quantity_values::zero()); lhs.numerical_value_ /= v; @@ -335,13 +290,9 @@ class quantity { } template Q2> - requires std::derived_from, quantity> && (Q2::unit == ::mp_units::one) && - requires(rep a, const typename Q2::rep b) { - { - a /= b - } -> std::same_as; - } - friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs) + requires std::derived_from, quantity> && + (Q2::unit == ::mp_units::one) && compound_field_for + friend constexpr decltype(auto) operator/=(Q1 && lhs, const Q2 & rhs) { gsl_ExpectsAudit(rhs != rhs.zero()); lhs.numerical_value_ /= rhs.numerical_value_; @@ -472,6 +423,9 @@ template return quantity>(std::forward(v)); } +template +struct vector_scalar : vector_scalar {}; + } // namespace mp_units namespace std { diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index 3a8dd8bd1..7093e794d 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -70,6 +70,7 @@ namespace detail { */ template auto PO, RepresentationOf Rep = double> + requires vector_space class quantity_point { public: // member types and values @@ -184,47 +185,45 @@ class quantity_point { // member unary operators template - friend constexpr decltype(auto) operator++(QP&& qp) - requires std::derived_from, quantity_point> && requires { ++qp.quantity_from_origin_; } + friend constexpr decltype(auto) operator++(QP && qp) + requires std::derived_from, quantity_point> && number_line { ++qp.quantity_from_origin_; return std::forward(qp); } [[nodiscard]] constexpr quantity_point operator++(int) - requires requires { quantity_from_origin_++; } + requires number_line { return quantity_point(quantity_from_origin_++); } template - friend constexpr decltype(auto) operator--(QP&& qp) - requires std::derived_from, quantity_point> && requires { --qp.quantity_from_origin_; } + friend constexpr decltype(auto) operator--(QP && qp) + requires std::derived_from, quantity_point> && number_line { --qp.quantity_from_origin_; return std::forward(qp); } [[nodiscard]] constexpr quantity_point operator--(int) - requires requires { quantity_from_origin_--; } + requires number_line { return quantity_point(quantity_from_origin_--); } // compound assignment operators template - requires std::derived_from, quantity_point> && - requires(quantity_type q) { quantity_from_origin_ += q; } - friend constexpr decltype(auto) operator+=(QP&& qp, const quantity_type& q) + requires std::derived_from, quantity_point> + friend constexpr decltype(auto) operator+=(QP && qp, const quantity_type & q) { qp.quantity_from_origin_ += q; return std::forward(qp); } template - requires std::derived_from, quantity_point> && - requires(quantity_type q) { quantity_from_origin_ -= q; } - friend constexpr decltype(auto) operator-=(QP&& qp, const quantity_type& q) + requires std::derived_from, quantity_point> + friend constexpr decltype(auto) operator-=(QP && qp, const quantity_type & q) { qp.quantity_from_origin_ -= q; return std::forward(qp); @@ -253,22 +252,20 @@ explicit quantity_point(QP) -> quantity_point::reference, quantity_point_like_traits::point_origin, typename quantity_point_like_traits::rep>; -template +template Rep2> // TODO simplify when gcc catches up requires ReferenceOf, PO1.quantity_spec> [[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point& qp, const quantity& q) - requires requires { qp.quantity_ref_from(PO1) + q; } { return make_quantity_point(qp.quantity_ref_from(PO1) + q); } -template +template Rep1> // TODO simplify when gcc catches up requires ReferenceOf, PO2.quantity_spec> [[nodiscard]] constexpr QuantityPoint auto operator+(const quantity& q, const quantity_point& qp) - requires requires { q + qp.quantity_ref_from(PO2); } { return qp + q; } @@ -287,12 +284,11 @@ template return po + std::forward(q); } -template +template Rep2> // TODO simplify when gcc catches up requires ReferenceOf, PO1.quantity_spec> [[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point& qp, const quantity& q) - requires requires { qp.quantity_ref_from(PO1) - q; } { return make_quantity_point(qp.quantity_ref_from(PO1) - q); } @@ -300,15 +296,14 @@ template template requires ReferenceOf, PO::quantity_spec> [[nodiscard]] constexpr QuantityPoint auto operator-(PO po, const Q& q) - requires requires { -q; } { return po + (-q); } template QP2> +// TODO consider constraining it for both branches + requires vector_space_for [[nodiscard]] constexpr Quantity auto operator-(const QP1& lhs, const QP2& rhs) - // TODO consider constraining it for both branches - requires requires { lhs.quantity_ref_from(QP1::point_origin) - rhs.quantity_ref_from(QP2::point_origin); } { if constexpr (is_same_v, std::remove_const_t>) @@ -398,4 +393,7 @@ template return quantity_point(std::forward(q)); } +template +struct enable_number : std::true_type {}; + } // namespace mp_units diff --git a/src/core/include/mp-units/reference.h b/src/core/include/mp-units/reference.h index 032c4192a..bf896e502 100644 --- a/src/core/include/mp-units/reference.h +++ b/src/core/include/mp-units/reference.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -155,9 +156,6 @@ struct reference { } }; -template Rep> -class quantity; - template [[nodiscard]] constexpr quantity> operator*(Rep&& lhs, R) { diff --git a/src/utility/include/mp-units/math.h b/src/utility/include/mp-units/math.h index 29254672e..635ab496e 100644 --- a/src/utility/include/mp-units/math.h +++ b/src/utility/include/mp-units/math.h @@ -274,7 +274,7 @@ template /** * @brief Computes the inverse of a quantity in a provided unit */ -template +template [[nodiscard]] constexpr QuantityOf auto inverse(const quantity& q) requires requires { quantity_values::one(); diff --git a/test/unit_test/runtime/CMakeLists.txt b/test/unit_test/runtime/CMakeLists.txt index ae7936b69..c808830bd 100644 --- a/test/unit_test/runtime/CMakeLists.txt +++ b/test/unit_test/runtime/CMakeLists.txt @@ -27,11 +27,11 @@ find_package(Catch2 3 CONFIG REQUIRED) add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp) target_link_libraries(unit_tests_runtime PRIVATE mp-units::mp-units Catch2::Catch2WithMain) -if(${projectPrefix}BUILD_LA) - find_package(wg21_linear_algebra CONFIG REQUIRED) - target_sources(unit_tests_runtime PRIVATE linear_algebra_test.cpp) - target_link_libraries(unit_tests_runtime PRIVATE wg21_linear_algebra::wg21_linear_algebra) -endif() +# if(${projectPrefix}BUILD_LA) +# find_package(wg21_linear_algebra CONFIG REQUIRED) +# target_sources(unit_tests_runtime PRIVATE linear_algebra_test.cpp) +# target_link_libraries(unit_tests_runtime PRIVATE wg21_linear_algebra::wg21_linear_algebra) +# endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options( diff --git a/test/unit_test/runtime/linear_algebra_test.cpp b/test/unit_test/runtime/linear_algebra_test.cpp index ad9fb31f4..b094fd296 100644 --- a/test/unit_test/runtime/linear_algebra_test.cpp +++ b/test/unit_test/runtime/linear_algebra_test.cpp @@ -34,12 +34,17 @@ template using vector = STD_LA::fixed_size_column_vector; +static_assert(mp_units::vector_space{3, 2, 1})>); + template inline constexpr bool mp_units::treat_as_floating_point> = mp_units::treat_as_floating_point; template inline constexpr bool mp_units::is_vector> = true; +template +struct mp_units::vector_scalar> : std::type_identity {}; + template std::ostream& operator<<(std::ostream& os, const vector& v) { diff --git a/test/unit_test/static/custom_rep_test_min_impl.cpp b/test/unit_test/static/custom_rep_test_min_impl.cpp index 7d18effd7..021b66d97 100644 --- a/test/unit_test/static/custom_rep_test_min_impl.cpp +++ b/test/unit_test/static/custom_rep_test_min_impl.cpp @@ -32,7 +32,7 @@ namespace { * * @tparam T element type */ -template +template class min_impl { T value_; public: @@ -45,6 +45,22 @@ class min_impl { { } constexpr explicit(false) operator T() const noexcept { return value_; } + + constexpr min_impl& operator+=(min_impl that) + { + value_ += that.value_; + return *this; + } + constexpr min_impl& operator-=(min_impl that) + { + value_ -= that.value_; + return *this; + } + constexpr min_impl& operator*=(T rhs) + { + value_ *= rhs; + return *this; + } }; } // namespace @@ -52,6 +68,9 @@ class min_impl { template inline constexpr bool mp_units::is_scalar> = true; +template +struct mp_units::vector_scalar> : std::type_identity {}; + template struct std::common_type, min_impl> : std::type_identity>> {}; template @@ -65,6 +84,8 @@ using namespace mp_units; static_assert(Representation>); static_assert(Representation>); +static_assert(vector_space>); +static_assert(vector_space>); // construction from a value is not allowed static_assert(!std::constructible_from>, min_impl>); diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index feb633d65..e71003690 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -130,6 +130,8 @@ static_assert(std::regular>); static_assert(std::three_way_comparable>); +static_assert(point_space>); + ////////////////// // member values diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 77091cb73..dd78b975e 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -85,6 +85,10 @@ static_assert(std::regular>); static_assert(std::three_way_comparable>); +static_assert(f_vector_space>); +static_assert(!scalar_number>, + "`std::common_with, double>` is `false`."); + ////////////////// // member values