mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-04 12:54:25 +02:00
Merge pull request #548 from mpusz/new_fmt
feat!: New formatting grammar
This commit is contained in:
27
.github/workflows/ci-conan.yml
vendored
27
.github/workflows/ci-conan.yml
vendored
@@ -35,11 +35,12 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||
name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
formatting: ["std::format", "fmtlib"]
|
||||
std: [20, 23]
|
||||
config:
|
||||
# - {
|
||||
@@ -65,6 +66,7 @@ jobs:
|
||||
cxx: "g++-12",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
conan-config: "",
|
||||
}
|
||||
- {
|
||||
@@ -78,6 +80,7 @@ jobs:
|
||||
cxx: "g++-13",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "True",
|
||||
conan-config: "",
|
||||
}
|
||||
- {
|
||||
@@ -92,6 +95,7 @@ jobs:
|
||||
},
|
||||
lib: "libc++",
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
conan-config: "",
|
||||
}
|
||||
- {
|
||||
@@ -106,6 +110,7 @@ jobs:
|
||||
},
|
||||
lib: "libc++",
|
||||
cxx_modules: "True",
|
||||
std_format_support: "True",
|
||||
conan-config: "",
|
||||
}
|
||||
- {
|
||||
@@ -119,9 +124,13 @@ jobs:
|
||||
cxx: "clang++",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
conan-config: "",
|
||||
}
|
||||
build_type: ["Release", "Debug"]
|
||||
exclude:
|
||||
- formatting: "std::format"
|
||||
config: { std_format_support: "False" }
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.config.compiler.cc }}
|
||||
@@ -137,13 +146,14 @@ jobs:
|
||||
cache-name: cache-conan-data
|
||||
with:
|
||||
path: ~/.conan2/p
|
||||
key: build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
|
||||
key: build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
|
||||
restore-keys: |
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-
|
||||
build-${{ matrix.config.os }}-
|
||||
- uses: hendrikmuhs/ccache-action@v1.2
|
||||
if: runner.os == 'Linux'
|
||||
@@ -196,12 +206,13 @@ jobs:
|
||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
|
||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
|
||||
conan profile show -pr default
|
||||
- run: echo "use_fmtlib=$([ "${{ matrix.formatting }}" == "fmtlib" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||
- name: Create Conan package
|
||||
shell: bash
|
||||
run: |
|
||||
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
||||
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
|
||||
-c user.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} ${{ matrix.config.conan-config }}
|
||||
-c user.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o use_fmtlib=${{ env.use_fmtlib }} ${{ matrix.config.conan-config }}
|
||||
- name: Obtain package reference
|
||||
id: get-package-ref
|
||||
shell: bash
|
||||
|
28
.github/workflows/ci-test-package-cmake.yml
vendored
28
.github/workflows/ci-test-package-cmake.yml
vendored
@@ -36,11 +36,12 @@ on:
|
||||
|
||||
jobs:
|
||||
test_package:
|
||||
name: "C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||
name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
formatting: ["std::format", "fmtlib"]
|
||||
std: [20, 23]
|
||||
config:
|
||||
# - {
|
||||
@@ -64,6 +65,7 @@ jobs:
|
||||
cxx: "g++-12",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
}
|
||||
- {
|
||||
name: "GCC-13",
|
||||
@@ -76,6 +78,7 @@ jobs:
|
||||
cxx: "g++-13",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "True",
|
||||
}
|
||||
- {
|
||||
name: "Clang-16",
|
||||
@@ -89,6 +92,7 @@ jobs:
|
||||
},
|
||||
lib: "libc++",
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
}
|
||||
- {
|
||||
name: "Clang-17",
|
||||
@@ -102,6 +106,7 @@ jobs:
|
||||
},
|
||||
lib: "libc++",
|
||||
cxx_modules: "False",
|
||||
std_format_support: "True",
|
||||
}
|
||||
- {
|
||||
name: "Apple Clang 15",
|
||||
@@ -114,8 +119,12 @@ jobs:
|
||||
cxx: "clang++",
|
||||
},
|
||||
cxx_modules: "False",
|
||||
std_format_support: "False",
|
||||
}
|
||||
build_type: ["Release", "Debug"]
|
||||
exclude:
|
||||
- formatting: "std::format"
|
||||
config: { std_format_support: "False" }
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.config.compiler.cc }}
|
||||
@@ -136,13 +145,14 @@ jobs:
|
||||
cache-name: cache-conan-data
|
||||
with:
|
||||
path: ~/.conan2/p
|
||||
key: build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
|
||||
key: build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
|
||||
restore-keys: |
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-
|
||||
build-${{ matrix.config.os }}-${{ matrix.formatting }}-
|
||||
build-${{ matrix.config.os }}-
|
||||
- name: Install gcc-13
|
||||
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '13'
|
||||
@@ -191,10 +201,12 @@ jobs:
|
||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
|
||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
|
||||
conan profile show -pr default
|
||||
- run: echo "use_fmtlib=$([ "${{ matrix.formatting }}" == "fmtlib" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||
- name: Install Conan dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.build:all=False -o cxx_modules=${{ matrix.config.cxx_modules }}
|
||||
conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.build:all=False \
|
||||
-o cxx_modules=${{ matrix.config.cxx_modules }} -o use_fmtlib=${{ env.use_fmtlib }}
|
||||
mv CMakeUserPresets.json src
|
||||
- name: Configure mp-units CMake
|
||||
if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -105,7 +105,7 @@ jobs:
|
||||
conan profile detect --force
|
||||
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
|
||||
mkdir _lgtm_build_dir && cd _lgtm_build_dir
|
||||
conan build .. -s compiler.cppstd=20 -c user.build:all=True -b missing
|
||||
conan build .. -s compiler.cppstd=20 -c user.build:all=True -o use_fmtlib=True -b missing
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
25
README.md
25
README.md
@@ -58,7 +58,7 @@ analysis and unit/quantity manipulation.
|
||||
Here is a small example of possible operations:
|
||||
|
||||
```cpp
|
||||
#include <mp-units/systems/si/si.h>
|
||||
import mp_units;
|
||||
|
||||
using namespace mp_units;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
@@ -66,7 +66,7 @@ using namespace mp_units::si::unit_symbols;
|
||||
// simple numeric operations
|
||||
static_assert(10 * km / 2 == 5 * km);
|
||||
|
||||
// unit conversions
|
||||
// conversions to common units
|
||||
static_assert(1 * h == 3600 * s);
|
||||
static_assert(1 * km + 1 * m == 1001 * m);
|
||||
|
||||
@@ -90,12 +90,9 @@ and dimensional analysis can be performed without sacrificing on runtime perform
|
||||
accuracy. Please see the below example for a quick preview of basic library features:
|
||||
|
||||
```cpp
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/ostream.h>
|
||||
#include <mp-units/systems/international/international.h>
|
||||
#include <mp-units/systems/isq/isq.h>
|
||||
#include <mp-units/systems/si/si.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
import mp_units;
|
||||
|
||||
using namespace mp_units;
|
||||
|
||||
@@ -118,13 +115,13 @@ int main()
|
||||
constexpr quantity v6 = value_cast<m / s>(v4);
|
||||
constexpr quantity v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
std::cout << std::format("{}", v3) << '\n'; // 110 km/h
|
||||
std::cout << std::format("{:*^14}", v4) << '\n'; // ***70 mi/h****
|
||||
std::cout << std::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s
|
||||
std::cout << std::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s
|
||||
std::cout << std::format("{:%Q}", v7) << '\n'; // 31
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
|
||||
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
|
||||
std::cout << std::format("{:%N in %U}\n", v4); // 70 in mi/h
|
||||
std::cout << std::format("{:{%N:.2f}%?%U}\n", v5); // 30.56 in m/s
|
||||
std::cout << std::format("{:{%N:.2f}%?{%U:n}}\n", v6); // 31.29 in m s⁻¹
|
||||
std::cout << std::format("{:%N}\n", v7); // 31
|
||||
}
|
||||
```
|
||||
|
||||
|
19
conanfile.py
19
conanfile.py
@@ -28,7 +28,6 @@ from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.build import check_min_cppstd
|
||||
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
|
||||
from conan.tools.files import copy, load, rmdir
|
||||
from conan.tools.scm import Version
|
||||
|
||||
required_conan_version = ">=2.0.0"
|
||||
|
||||
@@ -60,7 +59,7 @@ class MPUnitsConan(ConanFile):
|
||||
}
|
||||
default_options = {
|
||||
"cxx_modules": False,
|
||||
"use_fmtlib": True,
|
||||
"use_fmtlib": False,
|
||||
}
|
||||
tool_requires = "cmake/[>=3.28.1]"
|
||||
exports = ["LICENSE.md"]
|
||||
@@ -88,6 +87,15 @@ class MPUnitsConan(ConanFile):
|
||||
# , "msvc": "192"
|
||||
}
|
||||
|
||||
@property
|
||||
def _std_format_minimum_compilers_version(self):
|
||||
return {
|
||||
"gcc": "13",
|
||||
"clang": "17"
|
||||
# , "apple-clang": "15"
|
||||
# , "msvc": "192"
|
||||
}
|
||||
|
||||
@property
|
||||
def _build_all(self):
|
||||
return bool(self.conf.get("user.build:all", default=False))
|
||||
@@ -129,6 +137,13 @@ class MPUnitsConan(ConanFile):
|
||||
raise ConanInvalidConfiguration(
|
||||
f"{self.ref} requires at least {compiler} {min_version} ({compiler.version} in use)"
|
||||
)
|
||||
if not self.options.use_fmtlib:
|
||||
min_version = self._std_format_minimum_compilers_version.get(str(compiler))
|
||||
if min_version and loose_lt_semver(str(compiler.version), min_version):
|
||||
raise ConanInvalidConfiguration(
|
||||
f"`std::format` requires at least {compiler} {min_version} ({compiler.version} in use). "
|
||||
"Use `-o use_fmtlib=True` instead."
|
||||
)
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
@@ -23,7 +23,7 @@ specific feature:
|
||||
| Feature | gcc | clang | apple-clang | MSVC |
|
||||
|----------------------|:----:|:-----:|:-----------:|:----:|
|
||||
| **Minimum support** | 12 | 16 | 15 | None |
|
||||
| **`std::format`** | None | None | None | None |
|
||||
| **`std::format`** | 13 | 17 | None | None |
|
||||
| **C++ modules** | None | 17 | None | None |
|
||||
| **C++23 extensions** | 14 | 18 | None | None |
|
||||
|
||||
@@ -185,7 +185,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
|
||||
|
||||
[use_fmtlib](#use_fmtlib){ #use_fmtlib }
|
||||
|
||||
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `True`/`False` (Default: `True`)
|
||||
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
|
||||
|
||||
Forces usage of [{fmt}](https://github.com/fmtlib/fmt) library instead of the C++20 Standard
|
||||
Library features.
|
||||
@@ -232,7 +232,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
|
||||
|
||||
[`MP_UNITS_USE_FMTLIB`](#MP_UNITS_USE_FMTLIB){ #MP_UNITS_USE_FMTLIB }
|
||||
|
||||
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`)
|
||||
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
|
||||
|
||||
Forces usage of [{fmt}](https://github.com/fmtlib/fmt) library instead of the C++20 Standard
|
||||
Library features.
|
||||
|
@@ -13,7 +13,7 @@ Here is a small example of operations possible on scalar quantities:
|
||||
// simple numeric operations
|
||||
static_assert(10 * km / 2 == 5 * km);
|
||||
|
||||
// unit conversions
|
||||
// conversions to common units
|
||||
static_assert(1 * h == 3600 * s);
|
||||
static_assert(1 * km + 1 * m == 1001 * m);
|
||||
|
||||
@@ -40,7 +40,7 @@ Here is a small example of operations possible on scalar quantities:
|
||||
// simple numeric operations
|
||||
static_assert(10 * km / 2 == 5 * km);
|
||||
|
||||
// unit conversions
|
||||
// conversions to common units
|
||||
static_assert(1 * h == 3600 * s);
|
||||
static_assert(1 * km + 1 * m == 1001 * m);
|
||||
|
||||
@@ -56,7 +56,7 @@ Here is a small example of operations possible on scalar quantities:
|
||||
static_assert(1000 / (1 * s) == 1 * kHz);
|
||||
```
|
||||
|
||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/81Ev7qhTd)"
|
||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/ox8a8dGTz)"
|
||||
|
||||
|
||||
This library requires some C++20 features ([concepts and constraints](https://en.cppreference.com/w/cpp/language/constraints),
|
||||
@@ -69,6 +69,7 @@ performed without sacrificing accuracy. Please see the below example for a quick
|
||||
=== "C++ modules"
|
||||
|
||||
```cpp
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
import mp_units;
|
||||
|
||||
@@ -93,13 +94,13 @@ performed without sacrificing accuracy. Please see the below example for a quick
|
||||
constexpr quantity v6 = value_cast<m / s>(v4);
|
||||
constexpr quantity v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
std::cout << std::format("{}", v3) << '\n'; // 110 km/h
|
||||
std::cout << std::format("{:*^14}", v4) << '\n'; // ***70 mi/h****
|
||||
std::cout << std::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s
|
||||
std::cout << std::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s
|
||||
std::cout << std::format("{:%Q}", v7) << '\n'; // 31
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
|
||||
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
|
||||
std::cout << std::format("{:%N in %U}\n", v4); // 70 in mi/h
|
||||
std::cout << std::format("{:{%N:.2f}%?%U}\n", v5); // 30.56 in m/s
|
||||
std::cout << std::format("{:{%N:.2f}%?{%U:n}}\n", v6); // 31.29 in m s⁻¹
|
||||
std::cout << std::format("{:%N}\n", v7); // 31
|
||||
}
|
||||
```
|
||||
|
||||
@@ -111,6 +112,7 @@ performed without sacrificing accuracy. Please see the below example for a quick
|
||||
#include <mp-units/systems/international/international.h>
|
||||
#include <mp-units/systems/isq/isq.h>
|
||||
#include <mp-units/systems/si/si.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
using namespace mp_units;
|
||||
@@ -134,13 +136,13 @@ performed without sacrificing accuracy. Please see the below example for a quick
|
||||
constexpr quantity v6 = value_cast<m / s>(v4);
|
||||
constexpr quantity v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
std::cout << std::format("{}", v3) << '\n'; // 110 km/h
|
||||
std::cout << std::format("{:*^14}", v4) << '\n'; // ***70 mi/h****
|
||||
std::cout << std::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s
|
||||
std::cout << std::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s
|
||||
std::cout << std::format("{:%Q}", v7) << '\n'; // 31
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
|
||||
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
|
||||
std::cout << std::format("{:%N in %U}\n", v4); // 70 in mi/h
|
||||
std::cout << std::format("{:{%N:.2f}%?%U}\n", v5); // 30.56 in m/s
|
||||
std::cout << std::format("{:{%N:.2f}%?{%U:n}}\n", v6); // 31.29 in m s⁻¹
|
||||
std::cout << std::format("{:%N}\n", v7); // 31
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -31,7 +31,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units)
|
||||
| Feature | gcc | clang | apple-clang | MSVC |
|
||||
|----------------------|:----:|:-----:|:-----------:|:----:|
|
||||
| **Minimum support** | 12 | 16 | 15 | None |
|
||||
| **`std::format`** | None | None | None | None |
|
||||
| **`std::format`** | 13 | 17 | None | None |
|
||||
| **C++ modules** | None | 17 | None | None |
|
||||
| **C++23 extensions** | 14 | 18 | None | None |
|
||||
|
||||
|
@@ -18,20 +18,20 @@ First, we either import the `mp_units` module or include the headers for:
|
||||
- text formatting and stream output support
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="1"
|
||||
--8<-- "example/hello_units.cpp:28:39"
|
||||
--8<-- "example/hello_units.cpp:28:40"
|
||||
```
|
||||
|
||||
Also, to shorten the definitions, we "import" all the symbols from the `mp_units` namespace.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="12"
|
||||
--8<-- "example/hello_units.cpp:40:41"
|
||||
```cpp title="hello_units.cpp" linenums="13"
|
||||
--8<-- "example/hello_units.cpp:41:42"
|
||||
```
|
||||
|
||||
Next, we define a simple function that calculates the average speed based on the provided
|
||||
arguments of length and time:
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="13"
|
||||
--8<-- "example/hello_units.cpp:42:45"
|
||||
```cpp title="hello_units.cpp" linenums="14"
|
||||
--8<-- "example/hello_units.cpp:43:46"
|
||||
```
|
||||
|
||||
The above function template takes any quantities implicitly convertible to `isq::length`
|
||||
@@ -45,16 +45,16 @@ that its quantity type is implicitly convertible to `isq::speed`.
|
||||
type is beneficial for users of such a function as it provides more information
|
||||
of what to expect from a function than just using `auto`.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="17"
|
||||
--8<-- "example/hello_units.cpp:47:50"
|
||||
```cpp title="hello_units.cpp" linenums="18"
|
||||
--8<-- "example/hello_units.cpp:48:51"
|
||||
```
|
||||
|
||||
The above lines explicitly opt into using unit symbols from two systems of units.
|
||||
As this introduces a lot of short identifiers into the current scope, it is not done
|
||||
implicitly while including a header file.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="21"
|
||||
--8<-- "example/hello_units.cpp:52:58"
|
||||
```cpp title="hello_units.cpp" linenums="22"
|
||||
--8<-- "example/hello_units.cpp:53:59"
|
||||
```
|
||||
|
||||
- Lines `21` & `22` create a quantity of kind `isq::length / isq::time` with the numbers
|
||||
@@ -74,8 +74,8 @@ implicitly while including a header file.
|
||||
- Line `27` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions)
|
||||
of changing the underlying representation type from `double` to `int`.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="28"
|
||||
--8<-- "example/hello_units.cpp:60"
|
||||
```cpp title="hello_units.cpp" linenums="29"
|
||||
--8<-- "example/hello_units.cpp:61"
|
||||
```
|
||||
|
||||
The above presents [various ways to print a quantity](../framework_basics/text_output.md).
|
||||
|
@@ -72,4 +72,4 @@ add_example(total_energy)
|
||||
add_example(unmanned_aerial_vehicle example_utils)
|
||||
|
||||
add_subdirectory(glide_computer_lib)
|
||||
add_subdirectory(kalman_filter)
|
||||
# add_subdirectory(kalman_filter)
|
||||
|
@@ -118,7 +118,7 @@ void calcs_comparison()
|
||||
const auto L1A = 2.f * fm;
|
||||
const auto L2A = 3.f * fm;
|
||||
const auto LrA = L1A + L2A;
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30} %U}\n + {:{%N:.30} %U}\n = {:{%N:.30} %U}\n\n", L1A, L2A, LrA);
|
||||
|
||||
std::cout << "The single unit method must convert large\n"
|
||||
"or small values in other units to the base unit.\n"
|
||||
@@ -127,17 +127,17 @@ void calcs_comparison()
|
||||
const auto L1B = L1A.in(m);
|
||||
const auto L2B = L2A.in(m);
|
||||
const auto LrB = L1B + L2B;
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB);
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30e} %U}\n + {:{%N:.30e} %U}\n = {:{%N:.30e} %U}\n\n", L1B, L2B, LrB);
|
||||
|
||||
std::cout << "In multiplication and division:\n\n";
|
||||
|
||||
const quantity<isq::area[square(fm)], float> ArA = L1A * L2A;
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA);
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30} %U}\n * {:{%N:.30} %U}\n = {:{%N:.30} %U}\n\n", L1A, L2A, ArA);
|
||||
|
||||
std::cout << "similar problems arise\n\n";
|
||||
|
||||
const quantity<isq::area[m2], float> ArB = L1B * L2B;
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%.30eQ %q}\n * {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, ArB);
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30e} %U}\n * {:{%N:.30e} %U}\n = {:{%N:.30e} %U}\n\n", L1B, L2B, ArB);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -58,7 +58,7 @@ int main()
|
||||
|
||||
std::cout << MP_UNITS_STD_FMT::format("therefore ratio lengthA / lengthB == {}\n\n", lengthA / lengthB);
|
||||
|
||||
std::cout << MP_UNITS_STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("conversion factor from lengthA::unit of {:%U} to lengthB::unit of {:%U}:\n\n",
|
||||
lengthA, lengthB)
|
||||
<< MP_UNITS_STD_FMT::format("lengthB.value( {} ) == lengthA.value( {} ) * conversion_factor( {} )\n",
|
||||
lengthB.numerical_value_ref_in(lengthB.unit),
|
||||
|
@@ -87,8 +87,8 @@ void print(const R& gliders)
|
||||
std::cout << "- Polar:\n";
|
||||
for (const auto& p : g.polar) {
|
||||
const auto ratio = glide_ratio(g.polar[0]).force_in(one);
|
||||
std::cout << MP_UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v,
|
||||
ratio,
|
||||
std::cout << MP_UNITS_STD_FMT::format(" * {:{%N:.4} %U} @ {:{%N:.1} %U} -> {:{%N:.1} %U} ({:{%N:.1} %U})\n",
|
||||
p.climb, p.v, ratio,
|
||||
// TODO is it possible to make ADL work below (we need another set of trig
|
||||
// functions for strong angle in a different namespace)
|
||||
si::asin(1 / ratio).force_in(si::degree));
|
||||
@@ -106,8 +106,8 @@ void print(const R& conditions)
|
||||
for (const auto& c : conditions) {
|
||||
std::cout << "- " << c.first << "\n";
|
||||
const auto& w = c.second;
|
||||
std::cout << " * Cloud base: " << MP_UNITS_STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n";
|
||||
std::cout << " * Thermals strength: " << MP_UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
|
||||
std::cout << " * Cloud base: " << MP_UNITS_STD_FMT::format("{:{%N:.0} %U}", w.cloud_base) << " AGL\n";
|
||||
std::cout << " * Thermals strength: " << MP_UNITS_STD_FMT::format("{:{%N:.1} %U}", w.thermal_strength) << "\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ void print(const R& waypoints)
|
||||
std::cout << "Waypoints:\n";
|
||||
std::cout << "==========\n";
|
||||
for (const auto& w : waypoints)
|
||||
std::cout << MP_UNITS_STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
|
||||
std::cout << MP_UNITS_STD_FMT::format("- {}: {} {}, {:{%N:.1} %U}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ void print(const task& t)
|
||||
|
||||
std::cout << "- Start: " << t.get_start().name << "\n";
|
||||
std::cout << "- Finish: " << t.get_finish().name << "\n";
|
||||
std::cout << "- Length: " << MP_UNITS_STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n";
|
||||
std::cout << "- Length: " << MP_UNITS_STD_FMT::format("{:{%N:.1} %U}", t.get_distance()) << "\n";
|
||||
|
||||
std::cout << "- Legs: "
|
||||
<< "\n";
|
||||
for (const auto& l : t.get_legs())
|
||||
std::cout << MP_UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name,
|
||||
std::cout << MP_UNITS_STD_FMT::format(" * {} -> {} ({:{%N:.1} %U})\n", l.begin().name, l.end().name,
|
||||
l.get_distance());
|
||||
std::cout << "\n";
|
||||
}
|
||||
@@ -144,7 +144,7 @@ void print(const safety& s)
|
||||
{
|
||||
std::cout << "Safety:\n";
|
||||
std::cout << "=======\n";
|
||||
std::cout << "- Min AGL separation: " << MP_UNITS_STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n";
|
||||
std::cout << "- Min AGL separation: " << MP_UNITS_STD_FMT::format("{:{%N:.0} %U}", s.min_agl_height) << "\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
@@ -153,8 +153,8 @@ void print(const aircraft_tow& tow)
|
||||
std::cout << "Tow:\n";
|
||||
std::cout << "====\n";
|
||||
std::cout << "- Type: aircraft\n";
|
||||
std::cout << "- Height: " << MP_UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
|
||||
std::cout << "- Performance: " << MP_UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
|
||||
std::cout << "- Height: " << MP_UNITS_STD_FMT::format("{:{%N:.0} %U}", tow.height_agl) << "\n";
|
||||
std::cout << "- Performance: " << MP_UNITS_STD_FMT::format("{:{%N:.1} %U}", tow.performance) << "\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
|
@@ -82,7 +82,8 @@ void print(std::string_view phase_name, timestamp start_ts, const glide_computer
|
||||
const glide_computer::flight_point& new_point)
|
||||
{
|
||||
std::cout << MP_UNITS_STD_FMT::format(
|
||||
"| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
|
||||
"| {:<12} | {:>9{%N:.1} %U} (Total: {:>9{%N:.1} %U}) | {:>8{%N:.1} %U} (Total: {:>8{%N:.1} %U}) | {:>7{%N:.0} %U} "
|
||||
"({:>6{%N:.0} %U}) "
|
||||
"|\n",
|
||||
phase_name, value_cast<si::minute>(new_point.ts - point.ts), value_cast<si::minute>(new_point.ts - start_ts),
|
||||
new_point.dist - point.dist, new_point.dist, new_point.alt - point.alt, new_point.alt);
|
||||
|
@@ -26,6 +26,7 @@
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
#include <mp-units/compat_macros.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#ifdef MP_UNITS_MODULES
|
||||
import mp_units;
|
||||
@@ -57,11 +58,11 @@ int main()
|
||||
constexpr quantity v6 = value_cast<m / s>(v4);
|
||||
constexpr quantity v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
std::cout << MP_UNITS_STD_FMT::format("{}", v3) << '\n'; // 110 km/h
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:*^14}", v4) << '\n'; // ***70 mi/h****
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s
|
||||
std::cout << MP_UNITS_STD_FMT::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%Q}", v7) << '\n'; // 31
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:*^10}\n", v3); // *110 km/h*
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%N in %U}\n", v4); // 70 in mi/h
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.2f}%?%U}\n", v5); // 30.56 in m/s
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.2f}%?{%U:n}}\n", v6); // 31.29 in m s⁻¹
|
||||
std::cout << MP_UNITS_STD_FMT::format("{:%N}\n", v7); // 31
|
||||
}
|
||||
|
@@ -55,12 +55,14 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
|
||||
} // namespace geographic
|
||||
|
||||
template<>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::msl_altitude> : formatter<geographic::msl_altitude::quantity_type> {
|
||||
template<typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::msl_altitude, Char> :
|
||||
formatter<geographic::msl_altitude::quantity_type, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const geographic::msl_altitude& a, FormatContext& ctx)
|
||||
auto format(const geographic::msl_altitude& a, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
formatter<geographic::msl_altitude::quantity_type>::format(a - geographic::mean_sea_level, ctx);
|
||||
ctx.advance_to(
|
||||
formatter<geographic::msl_altitude::quantity_type, Char>::format(a - geographic::mean_sea_level, ctx));
|
||||
return MP_UNITS_STD_FMT::format_to(ctx.out(), " AMSL");
|
||||
}
|
||||
};
|
||||
@@ -137,29 +139,29 @@ class std::numeric_limits<geographic::longitude<T>> : public numeric_limits<T> {
|
||||
static constexpr auto max() noexcept { return geographic::longitude<T>(180); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::latitude<T>> :
|
||||
formatter<typename geographic::latitude<T>::quantity_type> {
|
||||
template<typename T, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::latitude<T>, Char> :
|
||||
formatter<typename geographic::latitude<T>::quantity_type, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(geographic::latitude<T> lat, FormatContext& ctx)
|
||||
auto format(geographic::latitude<T> lat, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
const auto& q = lat.quantity_ref_from(geographic::equator);
|
||||
formatter<typename geographic::latitude<T>::quantity_type>::format(is_gteq_zero(q) ? q : -q, ctx);
|
||||
MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(q) ? " N" : "S");
|
||||
return ctx.out();
|
||||
ctx.advance_to(
|
||||
formatter<typename geographic::latitude<T>::quantity_type, Char>::format(is_gteq_zero(q) ? q : -q, ctx));
|
||||
return MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(q) ? " N" : "S");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::longitude<T>> :
|
||||
formatter<typename geographic::longitude<T>::quantity_type> {
|
||||
template<typename T, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<geographic::longitude<T>, Char> :
|
||||
formatter<typename geographic::longitude<T>::quantity_type, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(geographic::longitude<T> lon, FormatContext& ctx)
|
||||
auto format(geographic::longitude<T> lon, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
const auto& q = lon.quantity_ref_from(geographic::prime_meridian);
|
||||
formatter<typename geographic::longitude<T>::quantity_type>::format(is_gteq_zero(q) ? q : -q, ctx);
|
||||
MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(q) ? " E" : " W");
|
||||
return ctx.out();
|
||||
ctx.advance_to(
|
||||
formatter<typename geographic::longitude<T>::quantity_type, Char>::format(is_gteq_zero(q) ? q : -q, ctx));
|
||||
return MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(q) ? " E" : " W");
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -63,11 +63,11 @@ template<typename T, auto Min, auto Max>
|
||||
inline constexpr bool mp_units::treat_as_floating_point<ranged_representation<T, Min, Max>> =
|
||||
mp_units::treat_as_floating_point<T>;
|
||||
|
||||
template<typename T, auto Min, auto Max>
|
||||
struct MP_UNITS_STD_FMT::formatter<ranged_representation<T, Min, Max>> : formatter<T> {
|
||||
template<typename T, auto Min, auto Max, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<ranged_representation<T, Min, Max>, Char> : formatter<T, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const ranged_representation<T, Min, Max>& v, FormatContext& ctx)
|
||||
auto format(const ranged_representation<T, Min, Max>& v, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
return formatter<T>::format(v.value(), ctx);
|
||||
return formatter<T, Char>::format(v.value(), ctx);
|
||||
}
|
||||
};
|
||||
|
@@ -123,11 +123,11 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename Validator>
|
||||
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>> : formatter<T> {
|
||||
template<typename T, typename Validator, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>, Char> : formatter<T, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const validated_type<T, Validator>& v, FormatContext& ctx)
|
||||
auto format(const validated_type<T, Validator>& v, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
return formatter<T>::format(v.value(), ctx);
|
||||
return formatter<T, Char>::format(v.value(), ctx);
|
||||
}
|
||||
};
|
||||
|
@@ -45,19 +45,19 @@ int main()
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
|
||||
std::cout << "The seven defining constants of the SI and the seven corresponding units they define:\n";
|
||||
std::cout << MP_UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:{%N:.0} %U}\n",
|
||||
1. * si2019::hyperfine_structure_transition_frequency_of_cs,
|
||||
(1. * si2019::hyperfine_structure_transition_frequency_of_cs).in(Hz));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:{%N:.0} %U}\n",
|
||||
1. * si2019::speed_of_light_in_vacuum,
|
||||
(1. * si2019::speed_of_light_in_vacuum).in(m / s));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Planck constant: {} = {:{%N:.8e} %U}\n",
|
||||
1. * si2019::planck_constant, (1. * si2019::planck_constant).in(J * s));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- elementary charge: {} = {:{%N:.9e} %U}\n",
|
||||
1. * si2019::elementary_charge, (1. * si2019::elementary_charge).in(C));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Boltzmann constant: {} = {:{%N:.6e} %U}\n",
|
||||
1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant).in(J / K));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n",
|
||||
std::cout << MP_UNITS_STD_FMT::format("- Avogadro constant: {} = {:{%N:.8e} %U}\n",
|
||||
1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(one / mol));
|
||||
std::cout << MP_UNITS_STD_FMT::format("- luminous efficacy: {} = {}\n",
|
||||
1. * si2019::luminous_efficacy, (1. * si2019::luminous_efficacy).in(lm / W));
|
||||
|
@@ -55,8 +55,8 @@ template<QuantityOf<isq::energy> T1, QuantityOf<isq::wavenumber> T2, QuantityOf<
|
||||
QuantityOf<isq::thermodynamic_temperature> T4, QuantityOf<isq::wavelength> T5>
|
||||
void print_line(const std::tuple<T1, T2, T3, T4, T5>& t)
|
||||
{
|
||||
MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", std::get<0>(t), std::get<1>(t),
|
||||
std::get<2>(t), std::get<3>(t), std::get<4>(t));
|
||||
std::cout << MP_UNITS_STD_FMT::format("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |\n", std::get<0>(t),
|
||||
std::get<1>(t), std::get<2>(t), std::get<3>(t), std::get<4>(t));
|
||||
}
|
||||
|
||||
// prints quantities in semi-SI units
|
||||
@@ -65,9 +65,9 @@ template<QuantityOf<isq::energy> T1, QuantityOf<isq::wavenumber> T2, QuantityOf<
|
||||
QuantityOf<isq::thermodynamic_temperature> T4, QuantityOf<isq::wavelength> T5>
|
||||
void print_line_si(const std::tuple<T1, T2, T3, T4, T5>& t)
|
||||
{
|
||||
MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", std::get<0>(t).in(eV),
|
||||
std::get<1>(t).in(one / cm), std::get<2>(t).in(THz), std::get<3>(t).in(K),
|
||||
std::get<4>(t).in(um));
|
||||
std::cout << MP_UNITS_STD_FMT::format("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |\n", std::get<0>(t).in(eV),
|
||||
std::get<1>(t).in(one / cm), std::get<2>(t).in(THz), std::get<3>(t).in(K),
|
||||
std::get<4>(t).in(um));
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -92,16 +92,16 @@ int main()
|
||||
const auto t5 = std::make_tuple(isq::energy(h * c / q5), isq::wavenumber(1 / q5), isq::frequency(c / q5),
|
||||
isq::thermodynamic_temperature(h * c / (q5 * kb)), q5);
|
||||
|
||||
MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", "Energy", "Wavenumber", "Frequency",
|
||||
"Temperature", "Wavelength");
|
||||
MP_UNITS_STD_FMT::println("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |", "");
|
||||
std::cout << MP_UNITS_STD_FMT::format("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |\n", "Energy", "Wavenumber",
|
||||
"Frequency", "Temperature", "Wavelength");
|
||||
std::cout << MP_UNITS_STD_FMT::format("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |\n", "");
|
||||
print_line(t1);
|
||||
print_line(t2);
|
||||
print_line(t3);
|
||||
print_line(t4);
|
||||
print_line(t5);
|
||||
|
||||
MP_UNITS_STD_FMT::println("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |", "");
|
||||
std::cout << MP_UNITS_STD_FMT::format("| {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} | {0:-^15} |\n", "");
|
||||
print_line_si(t1);
|
||||
print_line_si(t2);
|
||||
print_line_si(t3);
|
||||
|
@@ -79,13 +79,13 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
return os << a - a.absolute_point_origin << " HAE(" << to_text(a.absolute_point_origin.egm) << ")";
|
||||
}
|
||||
|
||||
template<QuantityPoint QP>
|
||||
template<QuantityPoint QP, typename Char>
|
||||
requires(is_hae(QP::absolute_point_origin))
|
||||
struct MP_UNITS_STD_FMT::formatter<QP> : formatter<typename QP::quantity_type> {
|
||||
struct MP_UNITS_STD_FMT::formatter<QP, Char> : formatter<typename QP::quantity_type, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const QP& a, FormatContext& ctx)
|
||||
auto format(const QP& a, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
formatter<typename QP::quantity_type>::format(a - a.absolute_point_origin, ctx);
|
||||
formatter<typename QP::quantity_type, Char>::format(a - a.absolute_point_origin, ctx);
|
||||
return MP_UNITS_STD_FMT::format_to(ctx.out(), " HAE({})", to_text(QP::absolute_point_origin.egm));
|
||||
}
|
||||
};
|
||||
@@ -123,12 +123,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
return os << a.quantity_from(height_above_launch) << " HAL";
|
||||
}
|
||||
|
||||
template<>
|
||||
struct MP_UNITS_STD_FMT::formatter<hal_altitude> : formatter<hal_altitude::quantity_type> {
|
||||
template<typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<hal_altitude, Char> : formatter<hal_altitude::quantity_type, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const hal_altitude& a, FormatContext& ctx)
|
||||
auto format(const hal_altitude& a, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
formatter<hal_altitude::quantity_type>::format(a.quantity_from(height_above_launch), ctx);
|
||||
formatter<hal_altitude::quantity_type, Char>::format(a.quantity_from(height_above_launch), ctx);
|
||||
return MP_UNITS_STD_FMT::format_to(ctx.out(), " HAL");
|
||||
}
|
||||
};
|
||||
@@ -170,6 +170,6 @@ int main()
|
||||
};
|
||||
|
||||
waypoint wpt = {"EPPR", {54.24772_N, 18.6745_E}, mean_sea_level + 16. * ft};
|
||||
std::cout << MP_UNITS_STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon,
|
||||
std::cout << MP_UNITS_STD_FMT::format("{}: {} {}, {:{%N:.2} %U}, {:{%N:.2} %U}\n", wpt.name, wpt.pos.lat, wpt.pos.lon,
|
||||
wpt.msl_alt, to_hae<earth_gravity_model::egm2008_1>(wpt.msl_alt, wpt.pos));
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_M
|
||||
option(${projectPrefix}AS_SYSTEM_HEADERS "Exports library as system headers" OFF)
|
||||
message(STATUS "${projectPrefix}AS_SYSTEM_HEADERS: ${${projectPrefix}AS_SYSTEM_HEADERS}")
|
||||
|
||||
option(${projectPrefix}USE_FMTLIB "Enables usage of fmtlib instead of the 'std::format' facilities" ON)
|
||||
option(${projectPrefix}USE_FMTLIB "Enables usage of fmtlib instead of the 'std::format' facilities" OFF)
|
||||
message(STATUS "${projectPrefix}USE_FMTLIB: ${${projectPrefix}USE_FMTLIB}")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
12
src/core/include/mp-units/bits/external/hacks.h
vendored
12
src/core/include/mp-units/bits/external/hacks.h
vendored
@@ -22,6 +22,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <version>
|
||||
|
||||
#if __clang__
|
||||
#define MP_UNITS_COMP_CLANG __clang_major__
|
||||
#elif __GNUC__
|
||||
@@ -114,8 +116,8 @@ MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
#define MP_UNITS_STD_FMT fmt
|
||||
#define MP_UNITS_FMT_LOCALE(loc) (loc).template get<std::locale>()
|
||||
#define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast<int>(arg)
|
||||
#define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast<size_t>(arg)
|
||||
#define MP_UNITS_FMT_TO_ARG_ID(arg) (arg)
|
||||
#define MP_UNITS_FMT_FROM_ARG_ID(arg) (arg)
|
||||
|
||||
// This re-uses code from fmt;
|
||||
#if FMT_EXCEPTIONS
|
||||
@@ -133,7 +135,7 @@ MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
#else
|
||||
|
||||
#ifndef __cpp_lib_format
|
||||
#if !defined __cpp_lib_format && !defined MP_UNITS_COMP_CLANG
|
||||
#error "std::formatting facility not supported"
|
||||
#endif
|
||||
|
||||
@@ -141,8 +143,8 @@ MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
#define MP_UNITS_STD_FMT std
|
||||
#define MP_UNITS_FMT_LOCALE(loc) loc
|
||||
#define MP_UNITS_FMT_TO_ARG_ID(arg) arg
|
||||
#define MP_UNITS_FMT_FROM_ARG_ID(arg) arg
|
||||
#define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast<std::size_t>(arg)
|
||||
#define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast<int>(arg)
|
||||
#define MP_UNITS_THROW(arg) throw arg
|
||||
|
||||
#endif
|
||||
|
@@ -35,15 +35,48 @@
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
// most of the below code is based on/copied from libfmt
|
||||
// most of the below code is based on/copied from fmtlib
|
||||
|
||||
namespace mp_units::detail {
|
||||
|
||||
struct auto_id {};
|
||||
enum class fmt_align { none, left, right, center, numeric };
|
||||
enum class fmt_arg_id_kind {
|
||||
none,
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
name,
|
||||
#endif
|
||||
index
|
||||
};
|
||||
|
||||
enum class fmt_align { none, left, right, center };
|
||||
enum class fmt_sign { none, minus, plus, space };
|
||||
enum class arg_id_kind { none, index, name };
|
||||
template<typename Char>
|
||||
struct fmt_arg_ref {
|
||||
fmt_arg_id_kind kind = fmt_arg_id_kind::none;
|
||||
union value {
|
||||
int index = 0;
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
std::basic_string_view<Char> name;
|
||||
#endif
|
||||
|
||||
constexpr value() {}
|
||||
constexpr value(int idx) : index(idx) {}
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
constexpr value(std::basic_string_view<Char> n) : name(n) {}
|
||||
#endif
|
||||
} val{};
|
||||
|
||||
fmt_arg_ref() = default;
|
||||
constexpr explicit fmt_arg_ref(int index) : kind(fmt_arg_id_kind::index), val(index) {}
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
constexpr explicit fmt_arg_ref(std::basic_string_view<Char> name) : kind(fmt_arg_id_kind::name), val(name) {}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] constexpr fmt_arg_ref& operator=(int idx)
|
||||
{
|
||||
kind = fmt_arg_id_kind::index;
|
||||
val.index = idx;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Char>
|
||||
struct fill_t {
|
||||
@@ -58,7 +91,7 @@ public:
|
||||
{
|
||||
auto size = str.size();
|
||||
if (size > max_size) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid fill"));
|
||||
for (size_t i = 0; i < size; ++i) data_[i] = str[i];
|
||||
for (size_t i = 0; i < size && i < max_size; ++i) data_[i] = str[i];
|
||||
size_ = static_cast<unsigned char>(size);
|
||||
return *this;
|
||||
}
|
||||
@@ -74,12 +107,6 @@ template<typename T>
|
||||
inline constexpr bool is_integer = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
|
||||
!std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value;
|
||||
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool is_ascii_letter(Char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
|
||||
template<std::integral Char>
|
||||
[[nodiscard]] constexpr Char to_ascii(Char value)
|
||||
@@ -89,232 +116,191 @@ template<std::integral Char>
|
||||
|
||||
template<typename Char>
|
||||
requires std::is_enum_v<Char>
|
||||
[[nodiscard]] constexpr auto to_ascii(Char value) -> std::underlying_type_t<Char>
|
||||
[[nodiscard]] constexpr std::underlying_type_t<Char> to_ascii(Char value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// Casts a nonnegative integer to unsigned.
|
||||
template<typename Int>
|
||||
[[nodiscard]] constexpr std::make_unsigned_t<Int> to_unsigned(Int value)
|
||||
{
|
||||
gsl_Expects(std::is_unsigned_v<Int> || value >= 0);
|
||||
return static_cast<std::make_unsigned_t<Int>>(value);
|
||||
}
|
||||
|
||||
struct width_checker {
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr unsigned long long operator()(T value) const
|
||||
{
|
||||
if constexpr (is_integer<T>) {
|
||||
if constexpr (std::numeric_limits<T>::is_signed) {
|
||||
if constexpr (std::numeric_limits<T>::is_signed)
|
||||
if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width"));
|
||||
}
|
||||
return static_cast<unsigned long long>(value);
|
||||
}
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer"));
|
||||
return 0; // should never happen
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct precision_checker {
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr unsigned long long operator()(T value) const
|
||||
{
|
||||
if constexpr (is_integer<T>) {
|
||||
if constexpr (std::numeric_limits<T>::is_signed) {
|
||||
if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative precision"));
|
||||
}
|
||||
return static_cast<unsigned long long>(value);
|
||||
}
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("precision is not integer"));
|
||||
return 0; // should never happen
|
||||
}
|
||||
};
|
||||
|
||||
// Format specifiers for built-in and string types.
|
||||
template<typename Char>
|
||||
struct basic_format_specs {
|
||||
int width = 0;
|
||||
int precision = -1;
|
||||
char type = '\0';
|
||||
fmt_align align : 4 = fmt_align::none;
|
||||
fmt_sign sign : 3 = fmt_sign::none;
|
||||
bool alt : 1 = false; // Alternate form ('#').
|
||||
bool localized : 1 = false;
|
||||
fill_t<Char> fill;
|
||||
};
|
||||
|
||||
// Format specifiers with width and precision resolved at formatting rather
|
||||
// than parsing time to allow re-using the same parsed specifiers with
|
||||
// different sets of arguments (precompilation of format strings).
|
||||
template<typename Char>
|
||||
struct dynamic_format_specs : basic_format_specs<Char> {
|
||||
int dynamic_width_index = -1;
|
||||
int dynamic_precision_index = -1;
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr int verify_dynamic_arg_index_in_range(size_t idx)
|
||||
template<class Handler, typename FormatArg>
|
||||
[[nodiscard]] constexpr int get_dynamic_spec(FormatArg arg)
|
||||
{
|
||||
if (idx > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("Dynamic width or precision index too large."));
|
||||
}
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
[[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, MP_UNITS_STD_FMT::basic_format_parse_context<CharT>& context)
|
||||
{
|
||||
context.check_arg_id(MP_UNITS_FMT_TO_ARG_ID(arg_id));
|
||||
return verify_dynamic_arg_index_in_range(arg_id);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
[[nodiscard]] constexpr int on_dynamic_arg(auto_id, MP_UNITS_STD_FMT::basic_format_parse_context<CharT>& context)
|
||||
{
|
||||
return verify_dynamic_arg_index_in_range(MP_UNITS_FMT_FROM_ARG_ID(context.next_arg_id()));
|
||||
}
|
||||
|
||||
template<class Handler, typename FormatContext>
|
||||
[[nodiscard]] constexpr int get_dynamic_spec(int index, FormatContext& ctx)
|
||||
{
|
||||
const unsigned long long value =
|
||||
MP_UNITS_STD_FMT::visit_format_arg(Handler{}, ctx.arg(MP_UNITS_FMT_TO_ARG_ID(static_cast<size_t>(index))));
|
||||
if (value > static_cast<unsigned long long>(std::numeric_limits<int>::max())) {
|
||||
const unsigned long long value = MP_UNITS_STD_FMT::visit_format_arg(Handler{}, arg);
|
||||
if (value > ::mp_units::detail::to_unsigned(std::numeric_limits<int>::max())) {
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("number is too big"));
|
||||
}
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
template<typename Context, typename ID>
|
||||
[[nodiscard]] constexpr auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id))
|
||||
{
|
||||
auto arg = ctx.arg(MP_UNITS_FMT_TO_ARG_ID(id));
|
||||
if (!arg) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("argument not found"));
|
||||
return arg;
|
||||
}
|
||||
|
||||
template<class Handler, typename Context>
|
||||
constexpr void handle_dynamic_spec(int& value, fmt_arg_ref<typename Context::char_type> ref, Context& ctx)
|
||||
{
|
||||
switch (ref.kind) {
|
||||
case fmt_arg_id_kind::none:
|
||||
break;
|
||||
case fmt_arg_id_kind::index:
|
||||
value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index));
|
||||
break;
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
case fmt_arg_id_kind::name:
|
||||
value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the range [begin, end) as an unsigned integer. This function assumes
|
||||
// that the range is non-empty and the first character is a digit.
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
[[nodiscard]] constexpr It parse_nonnegative_int(It begin, S end, size_t& value)
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr int parse_nonnegative_int(const Char*& begin, const Char* end, int error_value)
|
||||
{
|
||||
gsl_Expects(begin != end && '0' <= *begin && *begin <= '9');
|
||||
constexpr auto max_int = static_cast<unsigned>(std::numeric_limits<int>::max());
|
||||
constexpr auto big_int = max_int / 10u;
|
||||
value = 0;
|
||||
|
||||
unsigned value = 0, prev = 0;
|
||||
auto p = begin;
|
||||
do {
|
||||
if (value > big_int) {
|
||||
value = max_int + 1;
|
||||
break;
|
||||
}
|
||||
value = value * 10 + static_cast<unsigned int>(*begin - '0');
|
||||
++begin;
|
||||
} while (begin != end && '0' <= *begin && *begin <= '9');
|
||||
|
||||
if (value > max_int) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("Number is too big"));
|
||||
|
||||
return begin;
|
||||
prev = value;
|
||||
value = value * 10 + unsigned(*p - '0');
|
||||
++p;
|
||||
} while (p != end && '0' <= *p && *p <= '9');
|
||||
auto num_digits = p - begin;
|
||||
begin = p;
|
||||
if (num_digits <= std::numeric_limits<int>::digits10) return static_cast<int>(value);
|
||||
// Check for overflow.
|
||||
const unsigned max = ::mp_units::detail::to_unsigned((std::numeric_limits<int>::max)());
|
||||
return num_digits == std::numeric_limits<int>::digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max
|
||||
? static_cast<int>(value)
|
||||
: error_value;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
[[nodiscard]] constexpr It parse_nonnegative_int(It begin, S end, int& value)
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool is_name_start(Char c)
|
||||
{
|
||||
size_t val_unsigned = 0;
|
||||
begin = parse_nonnegative_int(begin, end, val_unsigned);
|
||||
// Never invalid because parse_nonnegative_integer throws an error for values that don't fit in signed integers
|
||||
value = static_cast<int>(val_unsigned);
|
||||
return begin;
|
||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename IDHandler>
|
||||
[[nodiscard]] constexpr It do_parse_arg_id(It begin, S end, IDHandler&& handler)
|
||||
template<typename Char, typename Handler>
|
||||
[[nodiscard]] constexpr const Char* do_parse_arg_id(const Char* begin, const Char* end, Handler&& handler)
|
||||
{
|
||||
gsl_Expects(begin != end);
|
||||
auto c = *begin;
|
||||
Char c = *begin;
|
||||
if (c >= '0' && c <= '9') {
|
||||
size_t index = 0;
|
||||
int index = 0;
|
||||
constexpr int max = (std::numeric_limits<int>::max)();
|
||||
if (c != '0')
|
||||
begin = parse_nonnegative_int(begin, end, index);
|
||||
index = ::mp_units::detail::parse_nonnegative_int(begin, end, max);
|
||||
else
|
||||
++begin;
|
||||
if (begin == end || (*begin != '}' && *begin != ':'))
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
else
|
||||
handler(index);
|
||||
handler.on_index(index);
|
||||
return begin;
|
||||
}
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
return begin; // should never happen
|
||||
if (c == '%') return begin; // mp-units extension
|
||||
if (!::mp_units::detail::is_name_start(c)) {
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
return begin;
|
||||
}
|
||||
auto it = begin;
|
||||
do {
|
||||
++it;
|
||||
} while (it != end && (::mp_units::detail::is_name_start(*it) || ('0' <= *it && *it <= '9')));
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
handler.on_name({begin, ::mp_units::detail::to_unsigned(it - begin)});
|
||||
#else
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("named arguments are not supported in the C++ standard facilities"));
|
||||
#endif
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename IDHandler>
|
||||
[[nodiscard]] constexpr It parse_arg_id(It begin, S end, IDHandler&& handler)
|
||||
{
|
||||
auto c = *begin;
|
||||
if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
|
||||
handler();
|
||||
return begin;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
[[nodiscard]] constexpr It parse_sign(It begin, S end, Handler&& handler)
|
||||
template<typename Char, typename Handler>
|
||||
[[nodiscard]] constexpr const Char* parse_arg_id(const Char* begin, const Char* end, Handler&& handler)
|
||||
{
|
||||
gsl_Expects(begin != end);
|
||||
switch (to_ascii(*begin)) {
|
||||
case '+':
|
||||
handler.on_sign(fmt_sign::plus);
|
||||
++begin;
|
||||
break;
|
||||
case '-':
|
||||
handler.on_sign(fmt_sign::minus);
|
||||
++begin;
|
||||
break;
|
||||
case ' ':
|
||||
handler.on_sign(fmt_sign::space);
|
||||
++begin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Char c = *begin;
|
||||
if (c != '}' && c != ':') return ::mp_units::detail::do_parse_arg_id(begin, end, handler);
|
||||
handler.on_auto();
|
||||
return begin;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
[[nodiscard]] constexpr It parse_width(It begin, S end, Handler&& handler)
|
||||
{
|
||||
struct width_adapter {
|
||||
Handler& handler;
|
||||
constexpr void operator()() { handler.on_dynamic_width(auto_id{}); }
|
||||
constexpr void operator()(size_t id) { handler.on_dynamic_width(id); }
|
||||
};
|
||||
template<typename Char>
|
||||
struct dynamic_spec_id_handler {
|
||||
MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx;
|
||||
fmt_arg_ref<Char>& ref;
|
||||
|
||||
constexpr void on_auto()
|
||||
{
|
||||
int id = MP_UNITS_FMT_FROM_ARG_ID(ctx.next_arg_id());
|
||||
ref = fmt_arg_ref<Char>(id);
|
||||
#if MP_UNITS_USE_FMTLIB || __cpp_lib_format >= 202305L
|
||||
ctx.check_dynamic_spec(id);
|
||||
#endif
|
||||
}
|
||||
constexpr void on_index(int id)
|
||||
{
|
||||
ref = fmt_arg_ref<Char>(id);
|
||||
ctx.check_arg_id(MP_UNITS_FMT_TO_ARG_ID(id));
|
||||
#if MP_UNITS_USE_FMTLIB || __cpp_lib_format >= 202305L
|
||||
ctx.check_dynamic_spec(id);
|
||||
#endif
|
||||
}
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
constexpr void on_name([[maybe_unused]] std::basic_string_view<Char> id)
|
||||
{
|
||||
ref = fmt_arg_ref<Char>(id);
|
||||
ctx.check_arg_id(id);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr const Char* parse_dynamic_spec(const Char* begin, const Char* end, int& value,
|
||||
fmt_arg_ref<Char>& ref,
|
||||
MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx)
|
||||
{
|
||||
gsl_Expects(begin != end);
|
||||
if ('0' <= *begin && *begin <= '9') {
|
||||
int width = 0;
|
||||
begin = parse_nonnegative_int(begin, end, width);
|
||||
if (width != -1)
|
||||
handler.on_width(width);
|
||||
int val = ::mp_units::detail::parse_nonnegative_int(begin, end, -1);
|
||||
if (val != -1)
|
||||
value = val;
|
||||
else
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("number is too big"));
|
||||
} else if (*begin == '{') {
|
||||
++begin;
|
||||
if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
|
||||
if (begin == end || *begin != '}') MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
++begin;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
[[nodiscard]] constexpr It parse_precision(It begin, S end, Handler&& handler)
|
||||
{
|
||||
struct precision_adapter {
|
||||
Handler& handler;
|
||||
constexpr void operator()() { handler.on_dynamic_precision(auto_id{}); }
|
||||
constexpr void operator()(size_t id) { handler.on_dynamic_precision(id); }
|
||||
};
|
||||
|
||||
++begin;
|
||||
auto c = begin != end ? *begin : std::iter_value_t<It>();
|
||||
if ('0' <= c && c <= '9') {
|
||||
auto precision = 0;
|
||||
begin = parse_nonnegative_int(begin, end, precision);
|
||||
if (precision != -1)
|
||||
handler.on_precision(precision);
|
||||
else
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("number is too big"));
|
||||
} else if (c == '{') {
|
||||
++begin;
|
||||
if (begin != end) begin = parse_arg_id(begin, end, precision_adapter{handler});
|
||||
if (begin == end || *begin++ != '}') MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
} else {
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("missing precision specifier"));
|
||||
if (*begin == '%') return begin - 1; // mp-units extension
|
||||
auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
|
||||
if (begin != end) begin = ::mp_units::detail::parse_arg_id(begin, end, handler);
|
||||
if (begin != end && *begin == '}') return ++begin;
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string"));
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
@@ -334,13 +320,14 @@ constexpr int code_point_length(It begin)
|
||||
}
|
||||
|
||||
// Parses fill and alignment.
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
[[nodiscard]] constexpr It parse_align(It begin, S end, Handler&& handler)
|
||||
template<typename Char, typename Specs>
|
||||
[[nodiscard]] constexpr const Char* parse_align(const Char* begin, const Char* end, Specs& specs,
|
||||
fmt_align default_align = fmt_align::none)
|
||||
{
|
||||
gsl_Expects(begin != end);
|
||||
auto align = fmt_align::none;
|
||||
auto p = begin + code_point_length(begin);
|
||||
if (p >= end) p = begin;
|
||||
if (end - p <= 0) p = begin;
|
||||
for (;;) {
|
||||
switch (to_ascii(*p)) {
|
||||
case '<':
|
||||
@@ -352,120 +339,29 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
case '^':
|
||||
align = fmt_align::center;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (align != fmt_align::none) {
|
||||
if (p != begin) {
|
||||
auto c = *begin;
|
||||
if (c == '{') MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid fill character '{'"));
|
||||
handler.on_fill(std::basic_string_view<std::iter_value_t<It>>(&*begin, static_cast<size_t>(p - begin)));
|
||||
if (c == '}') return begin;
|
||||
if (c == '{') {
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid fill character '{'"));
|
||||
return begin;
|
||||
}
|
||||
specs.fill = {begin, to_unsigned(p - begin)};
|
||||
begin = p + 1;
|
||||
} else
|
||||
} else {
|
||||
++begin;
|
||||
handler.on_align(align);
|
||||
}
|
||||
break;
|
||||
} else if (p == begin) {
|
||||
break;
|
||||
}
|
||||
p = begin;
|
||||
}
|
||||
if (align == fmt_align::none) align = default_align; // mp-units extension
|
||||
specs.align = align;
|
||||
return begin;
|
||||
}
|
||||
|
||||
// Parses standard format specifiers and sends notifications about parsed
|
||||
// components to handler.
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
|
||||
[[nodiscard]] constexpr It parse_format_specs(It begin, S end, SpecHandler&& handler)
|
||||
{
|
||||
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L') {
|
||||
handler.on_type(*begin++);
|
||||
return begin;
|
||||
}
|
||||
|
||||
if (begin == end) return begin;
|
||||
|
||||
begin = ::mp_units::detail::parse_align(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
// Parse sign.
|
||||
begin = ::mp_units::detail::parse_sign(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
if (*begin == '#') {
|
||||
handler.on_hash();
|
||||
if (++begin == end) return begin;
|
||||
}
|
||||
|
||||
// Parse zero flag.
|
||||
if (*begin == '0') {
|
||||
handler.on_zero();
|
||||
if (++begin == end) return begin;
|
||||
}
|
||||
|
||||
begin = ::mp_units::detail::parse_width(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
// Parse precision.
|
||||
if (*begin == '.') {
|
||||
begin = ::mp_units::detail::parse_precision(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
}
|
||||
|
||||
if (*begin == 'L') {
|
||||
handler.on_localized();
|
||||
++begin;
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (begin != end && *begin != '}') handler.on_type(*begin++);
|
||||
return begin;
|
||||
}
|
||||
|
||||
// A format specifier handler that sets fields in basic_format_specs.
|
||||
template<typename Char>
|
||||
class specs_setter {
|
||||
protected:
|
||||
basic_format_specs<Char>& specs_;
|
||||
public:
|
||||
constexpr explicit specs_setter(basic_format_specs<Char>& specs) : specs_(specs) {}
|
||||
constexpr void on_align(fmt_align align) { specs_.align = align; }
|
||||
constexpr void on_fill(std::basic_string_view<Char> fill) { specs_.fill = fill; }
|
||||
constexpr void on_sign(fmt_sign s) { specs_.sign = s; }
|
||||
constexpr void on_hash() { specs_.alt = true; }
|
||||
constexpr void on_localized() { specs_.localized = true; }
|
||||
constexpr void on_zero() { specs_.fill[0] = Char('0'); }
|
||||
constexpr void on_width(int width) { specs_.width = width; }
|
||||
constexpr void on_precision(int precision) { specs_.precision = precision; }
|
||||
constexpr void on_type(Char type) { specs_.type = static_cast<char>(type); }
|
||||
};
|
||||
|
||||
// Format spec handler that saves references to arguments representing dynamic
|
||||
// width and precision to be resolved at formatting time.
|
||||
template<typename ParseContext>
|
||||
class dynamic_specs_handler : public specs_setter<typename ParseContext::char_type> {
|
||||
public:
|
||||
using char_type = MP_UNITS_TYPENAME ParseContext::char_type;
|
||||
|
||||
constexpr dynamic_specs_handler(dynamic_format_specs<char_type>& specs, ParseContext& ctx) :
|
||||
specs_setter<char_type>(specs), specs_(specs), context_(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void on_dynamic_width(T t)
|
||||
{
|
||||
specs_.dynamic_width_index = on_dynamic_arg(t, context_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void on_dynamic_precision(T t)
|
||||
{
|
||||
specs_.dynamic_precision_index = on_dynamic_arg(t, context_);
|
||||
}
|
||||
private:
|
||||
dynamic_format_specs<char_type>& specs_;
|
||||
ParseContext& context_;
|
||||
};
|
||||
|
||||
} // namespace mp_units::detail
|
||||
|
@@ -29,185 +29,68 @@
|
||||
#include <mp-units/unit.h>
|
||||
#include <cstdint>
|
||||
|
||||
// Grammar
|
||||
//
|
||||
// quantity-format-spec ::= [fill-and-align] [width] [quantity-specs]
|
||||
// quantity-specs ::= conversion-spec
|
||||
// quantity-specs conversion-spec
|
||||
// quantity-specs literal-char
|
||||
// literal-char ::= any character other than '{' or '}'
|
||||
// conversion-spec ::= '%' type
|
||||
// type ::= [rep-modifier] 'Q'
|
||||
// [unit-modifier] 'q'
|
||||
// rep-modifier ::= [sign] [#] [precision] [L] [rep-type]
|
||||
// rep-type ::= one of
|
||||
// a A b B d e E f F g G o x X
|
||||
// unit-modifier ::= [text-encoding] [unit-symbol-solidus] [unit-symbol-separator]
|
||||
// [text-encoding] [unit-symbol-separator] [unit-symbol-solidus]
|
||||
// [unit-symbol-solidus] [text-encoding] [unit-symbol-separator]
|
||||
// [unit-symbol-solidus] [unit-symbol-separator] [text-encoding]
|
||||
// [unit-symbol-separator] [text-encoding] [unit-symbol-solidus]
|
||||
// [unit-symbol-separator] [unit-symbol-solidus] [text-encoding]
|
||||
// text-encoding ::= one of
|
||||
// U A
|
||||
// unit-symbol-solidus ::= one of
|
||||
// o a n
|
||||
// unit-symbol-separator ::= one of
|
||||
// s d
|
||||
|
||||
|
||||
// TODO Should the below be allowed? Is it even possible to implement with `format()` being const?
|
||||
// std::cout << std::format("{:%Q %q %Q %q}\n", 123s);
|
||||
|
||||
namespace mp_units::detail {
|
||||
|
||||
// Holds specs about the whole object
|
||||
template<typename CharT>
|
||||
struct quantity_global_format_specs {
|
||||
fill_t<CharT> fill;
|
||||
fmt_align align = fmt_align::none;
|
||||
template<typename Char>
|
||||
struct fill_align_width_format_specs {
|
||||
fill_t<Char> fill;
|
||||
fmt_align align : 4 = fmt_align::none;
|
||||
int width = 0;
|
||||
int dynamic_width_index = -1;
|
||||
fmt_arg_ref<Char> width_ref;
|
||||
};
|
||||
|
||||
// Holds specs about the representation (%[specs]Q)
|
||||
struct quantity_rep_format_specs {
|
||||
fmt_sign sign = fmt_sign::none;
|
||||
int precision = -1;
|
||||
int dynamic_precision_index = -1;
|
||||
char type = '\0';
|
||||
bool alt = false;
|
||||
bool localized = false;
|
||||
};
|
||||
|
||||
// Holds specs about the unit (%[specs]q)
|
||||
struct quantity_unit_format_specs : unit_symbol_formatting {};
|
||||
|
||||
template<typename CharT>
|
||||
struct quantity_format_specs {
|
||||
quantity_global_format_specs<CharT> global;
|
||||
quantity_rep_format_specs rep;
|
||||
quantity_unit_format_specs unit;
|
||||
};
|
||||
|
||||
// Parse a `units-rep-modifier`
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
constexpr It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_floating_point)
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr const Char* at_most_one_of(const Char* begin, const Char* end, std::string_view modifiers)
|
||||
{
|
||||
// parse sign
|
||||
begin = parse_sign(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
// parse #
|
||||
if (*begin == '#') {
|
||||
handler.on_hash();
|
||||
if (++begin == end) return begin;
|
||||
}
|
||||
|
||||
// parse precision if a floating point
|
||||
if (*begin == '.') {
|
||||
if (treat_as_floating_point) {
|
||||
begin = parse_precision(begin, end, handler);
|
||||
} else
|
||||
throw MP_UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation");
|
||||
if (begin == end) return begin;
|
||||
}
|
||||
|
||||
// parse L to enable the locale-specific form
|
||||
if (*begin == 'L') {
|
||||
handler.on_localized();
|
||||
++begin;
|
||||
}
|
||||
|
||||
if (begin != end && *begin != '}' && *begin != '%') {
|
||||
handler.on_type(*begin++);
|
||||
}
|
||||
return begin;
|
||||
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 MP_UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
|
||||
"' unit modifiers may be used in the format spec");
|
||||
return it;
|
||||
}
|
||||
|
||||
// parse units-specs
|
||||
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
|
||||
constexpr It parse_units_format(It begin, S end, Handler&& handler)
|
||||
template<typename Char, typename Specs>
|
||||
[[nodiscard]] constexpr const Char* parse_fill_align_width(MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx,
|
||||
const Char* begin, const Char* end, Specs& specs,
|
||||
fmt_align default_align = fmt_align::none)
|
||||
{
|
||||
auto ptr = begin;
|
||||
while (ptr != end) {
|
||||
auto c = *ptr;
|
||||
if (c == '}') break;
|
||||
if (c != '%') {
|
||||
++ptr;
|
||||
continue;
|
||||
}
|
||||
if (begin != ptr) handler.on_text(begin, ptr);
|
||||
begin = ++ptr; // consume '%'
|
||||
if (ptr == end) throw MP_UNITS_STD_FMT::format_error("invalid format");
|
||||
c = *ptr++;
|
||||
auto it = begin;
|
||||
if (it == end || *it == '}') return it;
|
||||
|
||||
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 MP_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);
|
||||
return ptr;
|
||||
it = mp_units::detail::parse_align(it, end, specs, default_align);
|
||||
if (it == end) return it;
|
||||
|
||||
return mp_units::detail::parse_dynamic_spec(it, end, specs.width, specs.width_ref, ctx);
|
||||
}
|
||||
|
||||
// build the 'representation' as requested in the format string, applying only units-rep-modifiers
|
||||
template<typename CharT, typename Rep, typename OutputIt, typename Locale>
|
||||
[[nodiscard]] OutputIt format_units_quantity_value(OutputIt out, const Rep& val,
|
||||
const quantity_rep_format_specs& rep_specs, const Locale& loc)
|
||||
template<typename Char, typename Handler>
|
||||
[[nodiscard]] constexpr const Char* parse_subentity_replacement_field(const Char* begin, const Char* end,
|
||||
Handler&& handler)
|
||||
{
|
||||
std::basic_string<CharT> buffer;
|
||||
auto to_buffer = std::back_inserter(buffer);
|
||||
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "{{:");
|
||||
switch (rep_specs.sign) {
|
||||
case fmt_sign::none:
|
||||
break;
|
||||
case fmt_sign::plus:
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "+");
|
||||
break;
|
||||
case fmt_sign::minus:
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "-");
|
||||
break;
|
||||
case fmt_sign::space:
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, " ");
|
||||
break;
|
||||
if (end - begin++ < 4)
|
||||
return MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("`subentity-replacement-field` too short")), end;
|
||||
if (*begin++ != '%')
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("`subentity-replacement-field` should start with '%'"));
|
||||
if (*begin == '}')
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("`subentity-replacement-field` should have an identifier"));
|
||||
auto it = begin;
|
||||
for (; it != end; ++it) {
|
||||
if (*it == '{' || *it == '%')
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid `subentity-replacement-field` format"));
|
||||
if (*it == '}' || *it == ':') break;
|
||||
}
|
||||
|
||||
if (rep_specs.alt) {
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "#");
|
||||
}
|
||||
auto type = rep_specs.type;
|
||||
if (auto precision = rep_specs.precision; precision >= 0) {
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type);
|
||||
} else if constexpr (treat_as_floating_point<Rep>) {
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type);
|
||||
} else {
|
||||
if (type != '\0') {
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "{}", type);
|
||||
}
|
||||
}
|
||||
if (rep_specs.localized) {
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "L");
|
||||
}
|
||||
|
||||
MP_UNITS_STD_FMT::format_to(to_buffer, "}}");
|
||||
if (rep_specs.localized) {
|
||||
return MP_UNITS_STD_FMT::vformat_to(out, MP_UNITS_FMT_LOCALE(loc), buffer, MP_UNITS_STD_FMT::make_format_args(val));
|
||||
}
|
||||
return MP_UNITS_STD_FMT::vformat_to(out, buffer, MP_UNITS_STD_FMT::make_format_args(val));
|
||||
if (it == end) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("`subentity-replacement-field` too short"));
|
||||
std::string_view id{begin, it};
|
||||
if (*it == ':') ++it;
|
||||
it = handler.on_replacement_field(id, it);
|
||||
if (it == end || *it != '}')
|
||||
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("`subentity-replacement-field` should end with '}'"));
|
||||
return ++it;
|
||||
}
|
||||
|
||||
// Creates a global format string
|
||||
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}"
|
||||
template<typename CharT, typename OutputIt>
|
||||
OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<CharT>& specs)
|
||||
template<typename OutputIt, typename Char>
|
||||
OutputIt format_global_buffer(OutputIt out, const fill_align_width_format_specs<Char>& specs)
|
||||
{
|
||||
MP_UNITS_STD_FMT::format_to(out, "{{:");
|
||||
if (specs.fill.size() != 1 || specs.fill[0] != ' ') {
|
||||
@@ -230,239 +113,346 @@ OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<C
|
||||
return MP_UNITS_STD_FMT::format_to(out, "}}");
|
||||
}
|
||||
|
||||
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, const quantity<Reference, Rep>& q, const quantity_format_specs<CharT>& fspecs,
|
||||
Locale lc) :
|
||||
out(o), val(q.numerical_value_ref_in(q.unit)), specs(fspecs), loc(std::move(lc))
|
||||
{
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
void on_text(It begin, S end)
|
||||
{
|
||||
std::copy(begin, end, out);
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
void on_quantity_value([[maybe_unused]] It, [[maybe_unused]] S)
|
||||
{
|
||||
out = format_units_quantity_value<CharT>(out, val, specs.rep, loc);
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
void on_quantity_unit(It, S)
|
||||
{
|
||||
out = unit_symbol_to<CharT>(out, get_unit(Reference), specs.unit);
|
||||
}
|
||||
};
|
||||
|
||||
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 MP_UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
|
||||
"' unit modifiers may be used in the format spec");
|
||||
return it;
|
||||
}
|
||||
|
||||
} // namespace mp_units::detail
|
||||
|
||||
template<auto Reference, typename Rep, typename CharT>
|
||||
struct MP_UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> {
|
||||
private:
|
||||
using quantity = mp_units::quantity<Reference, Rep>;
|
||||
using iterator = MP_UNITS_TYPENAME MP_UNITS_STD_FMT::basic_format_parse_context<CharT>::iterator;
|
||||
//
|
||||
// Grammar
|
||||
//
|
||||
// dimension-format-spec ::= [fill-and-align] [width] [dimension-spec]
|
||||
// dimension-spec ::= [text-encoding]
|
||||
// text-encoding ::= 'U' | 'A'
|
||||
//
|
||||
|
||||
bool quantity_value = false;
|
||||
bool quantity_unit = false;
|
||||
mp_units::detail::quantity_format_specs<CharT> specs;
|
||||
std::basic_string_view<CharT> format_str;
|
||||
// template<typename Char>
|
||||
// struct dimension_format_specs : fill_align_width_format_specs<Char>, dimension_symbol_formatting {};
|
||||
|
||||
struct spec_handler {
|
||||
formatter& f;
|
||||
MP_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(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(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; }
|
||||
//
|
||||
// Grammar
|
||||
//
|
||||
// unit-format-spec ::= [fill-and-align] [width] [unit-spec]
|
||||
// unit-spec ::= [text-encoding] [unit-symbol-solidus] [unit-symbol-separator] [L]
|
||||
// [text-encoding] [unit-symbol-separator] [unit-symbol-solidus] [L]
|
||||
// [unit-symbol-solidus] [text-encoding] [unit-symbol-separator] [L]
|
||||
// [unit-symbol-solidus] [unit-symbol-separator] [text-encoding] [L]
|
||||
// [unit-symbol-separator] [text-encoding] [unit-symbol-solidus] [L]
|
||||
// [unit-symbol-separator] [unit-symbol-solidus] [text-encoding] [L]
|
||||
// unit-symbol-solidus ::= '1' | 'a' | 'n'1
|
||||
// unit-symbol-separator ::= 's' | 'd'
|
||||
//
|
||||
template<mp_units::Unit U, typename Char>
|
||||
class MP_UNITS_STD_FMT::formatter<U, Char> {
|
||||
struct format_specs : mp_units::detail::fill_align_width_format_specs<Char>, mp_units::unit_symbol_formatting {};
|
||||
|
||||
constexpr void on_type(char type)
|
||||
std::basic_string_view<Char> fill_align_width_format_str_;
|
||||
std::basic_string_view<Char> modifiers_format_str_;
|
||||
format_specs specs_{};
|
||||
|
||||
struct format_checker {
|
||||
using enum mp_units::text_encoding;
|
||||
|
||||
mp_units::text_encoding encoding = unicode;
|
||||
|
||||
constexpr void on_text_encoding(Char val) { encoding = (val == 'U') ? unicode : ascii; }
|
||||
constexpr void on_unit_symbol_solidus(Char) const {}
|
||||
constexpr void on_unit_symbol_separator(Char val) const
|
||||
{
|
||||
constexpr auto valid_rep_types = std::string_view{"aAbBdeEfFgGoxX"};
|
||||
if (valid_rep_types.find(type) != std::string_view::npos) {
|
||||
f.specs.rep.type = type;
|
||||
} else {
|
||||
throw MP_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 = mp_units::detail::on_dynamic_arg(t, context);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void on_dynamic_precision(T t)
|
||||
{
|
||||
f.specs.rep.dynamic_precision_index = mp_units::detail::on_dynamic_arg(t, context);
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
constexpr void on_text(It, S)
|
||||
{
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
constexpr void on_quantity_value(It begin, S end)
|
||||
{
|
||||
if (begin != end) mp_units::detail::parse_units_rep(begin, end, *this, mp_units::treat_as_floating_point<Rep>);
|
||||
f.quantity_value = true;
|
||||
}
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
constexpr void on_quantity_unit(It begin, S end)
|
||||
{
|
||||
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 MP_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 MP_UNITS_STD_FMT::format_error("half_high_dot unit separator allowed only for Unicode encoding");
|
||||
f.specs.unit.separator = mp_units::unit_symbol_separator::half_high_dot;
|
||||
}
|
||||
}
|
||||
|
||||
f.quantity_unit = true;
|
||||
if (val == 'd' && encoding == ascii)
|
||||
throw MP_UNITS_STD_FMT::format_error("half_high_dot unit separator allowed only for Unicode encoding");
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr std::pair<iterator, iterator> do_parse(
|
||||
MP_UNITS_STD_FMT::basic_format_parse_context<CharT>& ctx)
|
||||
{
|
||||
auto begin = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
struct unit_formatter {
|
||||
format_specs specs;
|
||||
|
||||
if (begin == end || *begin == '}') return {begin, begin};
|
||||
using enum mp_units::text_encoding;
|
||||
using enum mp_units::unit_symbol_solidus;
|
||||
using enum mp_units::unit_symbol_separator;
|
||||
|
||||
// handler to assign parsed data to formatter data members
|
||||
spec_handler handler{*this, ctx};
|
||||
|
||||
// parse alignment
|
||||
begin = mp_units::detail::parse_align(begin, end, handler);
|
||||
if (begin == end) return {begin, begin};
|
||||
|
||||
// parse width
|
||||
begin = mp_units::detail::parse_width(begin, end, handler);
|
||||
if (begin == end) return {begin, begin};
|
||||
|
||||
// parse units-specific specification
|
||||
end = mp_units::detail::parse_units_format(begin, end, handler);
|
||||
|
||||
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 = mp_units::detail::fmt_align::right;
|
||||
|
||||
return {begin, end};
|
||||
}
|
||||
|
||||
template<typename OutputIt, typename FormatContext>
|
||||
OutputIt format_quantity_content(OutputIt out, const quantity& q, FormatContext& ctx)
|
||||
{
|
||||
auto begin = format_str.begin();
|
||||
auto end = format_str.end();
|
||||
|
||||
if (begin == end || *begin == '}') {
|
||||
// default format should print value followed by the unit separated with 1 space
|
||||
out = mp_units::detail::format_units_quantity_value<CharT>(out, q.numerical_value_ref_in(q.unit), specs.rep,
|
||||
ctx.locale());
|
||||
if constexpr (mp_units::detail::has_unit_symbol(get_unit(Reference))) {
|
||||
if constexpr (mp_units::space_before_unit_symbol<get_unit(Reference)>) *out++ = CharT(' ');
|
||||
out = unit_symbol_to<CharT>(out, get_unit(Reference));
|
||||
constexpr void on_text_encoding(Char val) { specs.encoding = (val == 'U') ? unicode : ascii; }
|
||||
constexpr void on_unit_symbol_solidus(Char val)
|
||||
{
|
||||
switch (val) {
|
||||
case '1':
|
||||
specs.solidus = one_denominator;
|
||||
break;
|
||||
case 'a':
|
||||
specs.solidus = always;
|
||||
break;
|
||||
case 'n':
|
||||
specs.solidus = never;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// user provided format
|
||||
mp_units::detail::quantity_formatter f(out, q, specs, ctx.locale());
|
||||
mp_units::detail::parse_units_format(begin, end, f);
|
||||
}
|
||||
return out;
|
||||
constexpr void on_unit_symbol_separator(Char val) { specs.separator = (val == 's') ? space : half_high_dot; }
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
constexpr const Char* parse_unit_specs(const Char* begin, const Char* end, Handler&& handler) const
|
||||
{
|
||||
auto it = begin;
|
||||
if (it == end || *it == '}') return begin;
|
||||
|
||||
constexpr auto valid_modifiers = std::string_view{"UA1ansd"};
|
||||
for (; it != end && *it != '}'; ++it) {
|
||||
if (valid_modifiers.find(*it) == std::string_view::npos)
|
||||
throw MP_UNITS_STD_FMT::format_error("invalid unit modifier specified");
|
||||
}
|
||||
end = it;
|
||||
|
||||
if (it = mp_units::detail::at_most_one_of(begin, end, "UA"); it != end) handler.on_text_encoding(*it);
|
||||
if (it = mp_units::detail::at_most_one_of(begin, end, "1an"); it != end) handler.on_unit_symbol_solidus(*it);
|
||||
if (it = mp_units::detail::at_most_one_of(begin, end, "sd"); it != end) handler.on_unit_symbol_separator(*it);
|
||||
return end;
|
||||
}
|
||||
|
||||
public:
|
||||
[[nodiscard]] constexpr auto parse(MP_UNITS_STD_FMT::basic_format_parse_context<CharT>& ctx)
|
||||
constexpr auto parse(MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
auto range = do_parse(ctx);
|
||||
if (range.first != range.second)
|
||||
format_str = std::basic_string_view<CharT>(&*range.first, static_cast<size_t>(range.second - range.first));
|
||||
return range.second;
|
||||
const auto begin = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
|
||||
auto it = parse_fill_align_width(ctx, begin, end, specs_);
|
||||
fill_align_width_format_str_ = {begin, it};
|
||||
if (it == end) return it;
|
||||
|
||||
format_checker checker;
|
||||
end = parse_unit_specs(it, end, checker);
|
||||
modifiers_format_str_ = {it, end};
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const quantity& q, FormatContext& ctx)
|
||||
auto format(const U& u, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
// process dynamic width and precision
|
||||
if (specs.global.dynamic_width_index >= 0)
|
||||
specs.global.width =
|
||||
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 =
|
||||
mp_units::detail::get_dynamic_spec<mp_units::detail::precision_checker>(specs.rep.dynamic_precision_index, ctx);
|
||||
unit_formatter f{specs_};
|
||||
mp_units::detail::handle_dynamic_spec<mp_units::detail::width_checker>(f.specs.width, f.specs.width_ref, ctx);
|
||||
|
||||
if (specs.global.width == 0) {
|
||||
parse_unit_specs(modifiers_format_str_.begin(), modifiers_format_str_.end(), f);
|
||||
|
||||
if (f.specs.width == 0) {
|
||||
// Avoid extra copying if width is not specified
|
||||
return format_quantity_content(ctx.out(), q, ctx);
|
||||
return mp_units::unit_symbol_to<Char>(ctx.out(), u, f.specs);
|
||||
} else {
|
||||
// In `quantity_buffer` we will have the representation and the unit formatted according to their
|
||||
// specification, ignoring global specifiers
|
||||
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "1.2_m"
|
||||
std::basic_string<CharT> quantity_buffer;
|
||||
std::basic_string<Char> unit_buffer;
|
||||
mp_units::unit_symbol_to<Char>(std::back_inserter(unit_buffer), u, f.specs);
|
||||
|
||||
// deal with quantity content
|
||||
format_quantity_content(std::back_inserter(quantity_buffer), q, ctx);
|
||||
|
||||
// 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;
|
||||
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 MP_UNITS_STD_FMT::format("{:*^10}", "1.2_m")
|
||||
std::basic_string<Char> global_format_buffer = "{:" + std::basic_string<Char>{fill_align_width_format_str_} + "}";
|
||||
return MP_UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer,
|
||||
MP_UNITS_STD_FMT::make_format_args(unit_buffer));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Grammar
|
||||
//
|
||||
// quantity-format-spec ::= [fill-and-align] [width] [quantity-specs]
|
||||
// quantity-specs ::= conversion-spec
|
||||
// quantity-specs conversion-spec
|
||||
// quantity-specs literal-char
|
||||
// literal-char ::= <any character other than '{', '}', or '%'>
|
||||
// conversion-spec ::= placement-spec
|
||||
// subentity-replacement-field
|
||||
// placement-spec ::= '%' placement-type
|
||||
// placement-type ::= 'N' | 'U' | 'D' | '?' | '%'
|
||||
// subentity-replacement-field ::= '{' '%' subentity-id [format-specifier] '}'
|
||||
// subentity-id ::= literal-char
|
||||
// subentity-id literal-char
|
||||
// format-specifier ::= ':' format-spec
|
||||
// format-spec ::= <as specified by the formatter for the argument type; cannot start with '}'>
|
||||
//
|
||||
template<auto Reference, typename Rep, typename Char>
|
||||
class MP_UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, Char> {
|
||||
static constexpr auto unit = get_unit(Reference);
|
||||
static constexpr auto dimension = get_quantity_spec(Reference).dimension;
|
||||
|
||||
using quantity_t = mp_units::quantity<Reference, Rep>;
|
||||
using unit_t = std::remove_const_t<decltype(unit)>;
|
||||
using dimension_t = std::remove_const_t<decltype(dimension)>;
|
||||
|
||||
using format_specs = mp_units::detail::fill_align_width_format_specs<Char>;
|
||||
|
||||
std::basic_string_view<Char> modifiers_format_str_;
|
||||
std::vector<size_t> format_str_lengths_;
|
||||
format_specs specs_{};
|
||||
|
||||
struct format_checker {
|
||||
MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx;
|
||||
std::vector<size_t>& format_str_lengths;
|
||||
|
||||
constexpr void on_number(std::basic_string_view<Char>) const {}
|
||||
constexpr void on_maybe_space() const {}
|
||||
constexpr void on_unit(std::basic_string_view<Char>) const {}
|
||||
constexpr void on_dimension(std::basic_string_view<Char>) const {}
|
||||
constexpr void on_text(const Char*, const Char*) const {}
|
||||
|
||||
constexpr const Char* on_replacement_field(std::basic_string_view<Char> id, const Char* begin)
|
||||
{
|
||||
if (id == "N")
|
||||
return on_replacement_field<Rep>(begin);
|
||||
else if (id == "U")
|
||||
return on_replacement_field<unit_t>(begin);
|
||||
else if (id == "D") {
|
||||
return begin;
|
||||
// on_replacement_field<dimension_t>(begin);
|
||||
} else
|
||||
throw MP_UNITS_STD_FMT::format_error("unknown replacement field '" + std::string(id) + "'");
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
constexpr const Char* on_replacement_field(const Char* begin) const
|
||||
{
|
||||
MP_UNITS_STD_FMT::formatter<T> sf;
|
||||
ctx.advance_to(begin);
|
||||
auto ptr = sf.parse(ctx);
|
||||
if (*ptr != '}') throw MP_UNITS_STD_FMT::format_error("unmatched '}' in format string");
|
||||
format_str_lengths.push_back(mp_units::detail::to_unsigned(ptr - begin));
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OutputIt>
|
||||
struct quantity_formatter {
|
||||
OutputIt out;
|
||||
const quantity_t& q;
|
||||
std::vector<size_t>::const_iterator format_str_lengths_it;
|
||||
std::locale locale;
|
||||
|
||||
void on_number(std::basic_string_view<Char> format_str)
|
||||
{
|
||||
out = MP_UNITS_STD_FMT::vformat_to(out, locale, format_str,
|
||||
MP_UNITS_STD_FMT::make_format_args(q.numerical_value_ref_in(q.unit)));
|
||||
}
|
||||
void on_maybe_space()
|
||||
{
|
||||
if constexpr (mp_units::space_before_unit_symbol<unit>) *out++ = ' ';
|
||||
}
|
||||
void on_unit(std::basic_string_view<Char> format_str)
|
||||
{
|
||||
out = MP_UNITS_STD_FMT::vformat_to(out, locale, format_str, MP_UNITS_STD_FMT::make_format_args(q.unit));
|
||||
}
|
||||
void on_dimension(std::basic_string_view<Char>) {}
|
||||
void on_text(const Char* begin, const Char* end) const { std::copy(begin, end, out); }
|
||||
|
||||
constexpr const Char* on_replacement_field(std::basic_string_view<Char> id, const Char* begin)
|
||||
{
|
||||
auto format_str = [&] { return "{:" + std::string(begin, *format_str_lengths_it + 1); };
|
||||
if (id == "N")
|
||||
on_number(format_str());
|
||||
else if (id == "U")
|
||||
on_unit(format_str());
|
||||
else if (id == "D")
|
||||
on_dimension(format_str());
|
||||
else
|
||||
throw MP_UNITS_STD_FMT::format_error("unknown replacement field '" + std::string(id) + "'");
|
||||
return begin + *format_str_lengths_it++;
|
||||
}
|
||||
};
|
||||
template<typename OutputIt, typename... Args>
|
||||
quantity_formatter(OutputIt, Args...) -> quantity_formatter<OutputIt>;
|
||||
|
||||
template<typename Handler>
|
||||
constexpr const Char* parse_quantity_specs(const Char* begin, const Char* end, Handler&& handler) const
|
||||
{
|
||||
if (begin == end || *begin == '}') return begin;
|
||||
if (*begin != '%' && *begin != '{')
|
||||
throw MP_UNITS_STD_FMT::format_error(
|
||||
"`quantity-specs` should start with a `conversion-spec` ('%' or '{' characters expected)})");
|
||||
auto ptr = begin;
|
||||
while (ptr != end) {
|
||||
auto c = *ptr;
|
||||
if (c == '}') break;
|
||||
if (c == '{') {
|
||||
if (begin != ptr) handler.on_text(begin, ptr);
|
||||
begin = ptr = mp_units::detail::parse_subentity_replacement_field(ptr, end, handler);
|
||||
continue;
|
||||
}
|
||||
if (c != '%') {
|
||||
++ptr;
|
||||
continue;
|
||||
}
|
||||
if (begin != ptr) handler.on_text(begin, ptr);
|
||||
++ptr; // consume '%'
|
||||
if (ptr == end) throw MP_UNITS_STD_FMT::format_error("invalid `placement-spec` format");
|
||||
|
||||
c = *ptr++;
|
||||
switch (c) {
|
||||
case 'N':
|
||||
handler.on_number("{}");
|
||||
break;
|
||||
case 'U':
|
||||
handler.on_unit("{}");
|
||||
break;
|
||||
case 'D':
|
||||
handler.on_dimension("{}");
|
||||
break;
|
||||
case '?':
|
||||
handler.on_maybe_space();
|
||||
break;
|
||||
case '%':
|
||||
handler.on_text(ptr - 1, ptr);
|
||||
break;
|
||||
default:
|
||||
throw MP_UNITS_STD_FMT::format_error(std::string("unknown `placement-spec` token '") + c + "'");
|
||||
}
|
||||
begin = ptr;
|
||||
}
|
||||
if (begin != ptr) handler.on_text(begin, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<typename OutputIt, typename FormatContext>
|
||||
OutputIt format_quantity(OutputIt out, const quantity_t& q, FormatContext& ctx) const
|
||||
{
|
||||
std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale());
|
||||
if (modifiers_format_str_.empty()) {
|
||||
// default format should print value followed by the unit separated with 1 space
|
||||
out = MP_UNITS_STD_FMT::vformat_to(out, locale, "{}",
|
||||
MP_UNITS_STD_FMT::make_format_args(q.numerical_value_ref_in(q.unit)));
|
||||
if constexpr (mp_units::space_before_unit_symbol<unit>) *out++ = ' ';
|
||||
return MP_UNITS_STD_FMT::vformat_to(out, locale, "{}", MP_UNITS_STD_FMT::make_format_args(q.unit));
|
||||
} else {
|
||||
// user provided format
|
||||
quantity_formatter f{out, q, format_str_lengths_.cbegin(), locale};
|
||||
parse_quantity_specs(modifiers_format_str_.begin(), modifiers_format_str_.end(), f);
|
||||
return f.out;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr auto parse(MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
const auto begin = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
|
||||
auto it = parse_fill_align_width(ctx, begin, end, specs_, mp_units::detail::fmt_align::right);
|
||||
if (it == end) return it;
|
||||
|
||||
format_checker checker{ctx, format_str_lengths_};
|
||||
end = parse_quantity_specs(it, end, checker);
|
||||
modifiers_format_str_ = {it, end};
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const quantity_t& q, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
auto specs = specs_;
|
||||
mp_units::detail::handle_dynamic_spec<mp_units::detail::width_checker>(specs.width, specs.width_ref, ctx);
|
||||
|
||||
if (specs.width == 0) {
|
||||
// Avoid extra copying if width is not specified
|
||||
return format_quantity(ctx.out(), q, ctx);
|
||||
} else {
|
||||
std::basic_string<Char> quantity_buffer;
|
||||
format_quantity(std::back_inserter(quantity_buffer), q, ctx);
|
||||
|
||||
std::basic_string<Char> fill_align_width_format_str;
|
||||
mp_units::detail::format_global_buffer(std::back_inserter(fill_align_width_format_str), specs);
|
||||
return MP_UNITS_STD_FMT::vformat_to(ctx.out(), fill_align_width_format_str,
|
||||
MP_UNITS_STD_FMT::make_format_args(quantity_buffer));
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,12 @@ namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename CharT, class Traits, Unit U>
|
||||
void to_stream(std::basic_ostream<CharT, Traits>& os, U u)
|
||||
{
|
||||
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), u);
|
||||
}
|
||||
|
||||
template<typename CharT, class Traits, auto R, typename Rep>
|
||||
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
|
||||
{
|
||||
@@ -39,14 +45,29 @@ void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
|
||||
os << +q.numerical_value_ref_in(q.unit);
|
||||
else
|
||||
os << q.numerical_value_ref_in(q.unit);
|
||||
if constexpr (has_unit_symbol(get_unit(R))) {
|
||||
if constexpr (space_before_unit_symbol<get_unit(R)>) os << " ";
|
||||
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
|
||||
}
|
||||
if constexpr (space_before_unit_symbol<get_unit(R)>) os << " ";
|
||||
to_stream(os, get_unit(R));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename CharT, typename Traits, Unit U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, U u)
|
||||
{
|
||||
if (os.width()) {
|
||||
// std::setw() applies to the whole uni output so it has to be first put into std::string
|
||||
std::basic_ostringstream<CharT, Traits> oss;
|
||||
oss.flags(os.flags());
|
||||
oss.imbue(os.getloc());
|
||||
oss.precision(os.precision());
|
||||
detail::to_stream(oss, u);
|
||||
return os << std::move(oss).str();
|
||||
}
|
||||
|
||||
detail::to_stream(os, u);
|
||||
return os;
|
||||
}
|
||||
|
||||
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.numerical_value_ref_in(q.unit); }
|
||||
|
@@ -653,6 +653,23 @@ template<Unit U1, Unit U2>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Puts a space ' ' sign before a unit symbol
|
||||
*
|
||||
* Quantities of some units (e.g. degree, arcminute, arcsecond) should not be printed with the
|
||||
* space between a number and a unit. For those a partial specialization with the value `false` should
|
||||
* be provided.
|
||||
*/
|
||||
template<Unit auto U>
|
||||
inline constexpr bool space_before_unit_symbol = true;
|
||||
|
||||
template<>
|
||||
inline constexpr bool space_before_unit_symbol<one> = false;
|
||||
template<>
|
||||
inline constexpr bool space_before_unit_symbol<percent> = false;
|
||||
template<>
|
||||
inline constexpr bool space_before_unit_symbol<per_mille> = false;
|
||||
|
||||
// get_unit_symbol
|
||||
|
||||
enum class text_encoding : std::int8_t {
|
||||
@@ -713,12 +730,6 @@ constexpr Out print_separator(Out out, unit_symbol_formatting fmt)
|
||||
return out;
|
||||
}
|
||||
|
||||
template<Unit U>
|
||||
[[nodiscard]] consteval bool has_unit_symbol(U)
|
||||
{
|
||||
return requires { U::symbol; } || !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)
|
||||
@@ -741,12 +752,8 @@ constexpr Out unit_symbol_impl(Out out, const scaled_unit<M, U>& u, unit_symbol_
|
||||
constexpr auto mag_txt = magnitude_text<M>();
|
||||
out = copy<CharT>(mag_txt, fmt.encoding, out);
|
||||
|
||||
if constexpr (has_unit_symbol(scaled_unit<M, U>::reference_unit)) {
|
||||
*out++ = ' ';
|
||||
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
if constexpr (space_before_unit_symbol<scaled_unit<M, U>::reference_unit>) *out++ = ' ';
|
||||
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,21 +835,6 @@ constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, unit_symbo
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Puts a space ' ' sign before a unit symbol
|
||||
*
|
||||
* Quantities of some units (e.g. degree, arcminute, arcsecond) should not be printed with the
|
||||
* space between a number and a unit. For those a partial specialization with the value `false` should
|
||||
* be provided.
|
||||
*/
|
||||
template<Unit auto U>
|
||||
inline constexpr bool space_before_unit_symbol = true;
|
||||
|
||||
template<>
|
||||
inline constexpr bool space_before_unit_symbol<percent> = false;
|
||||
template<>
|
||||
inline constexpr bool space_before_unit_symbol<per_mille> = false;
|
||||
|
||||
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{})
|
||||
{
|
||||
|
@@ -33,6 +33,7 @@ import mp_units;
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/ostream.h>
|
||||
#include <mp-units/systems/cgs/cgs.h>
|
||||
#include <mp-units/systems/isq/electromagnetism.h>
|
||||
#include <mp-units/systems/isq/mechanics.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/si.h>
|
||||
@@ -60,7 +61,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("floating-point representation")
|
||||
@@ -72,7 +76,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +92,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str()); }
|
||||
}
|
||||
|
||||
SECTION("quantity with a derived unit")
|
||||
@@ -101,9 +108,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +123,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +138,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,9 +156,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +171,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,9 +186,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,9 +201,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity")
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +220,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "2 "); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == "2"); }
|
||||
}
|
||||
|
||||
SECTION("one with ratio.exp != 0")
|
||||
@@ -225,19 +232,27 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "2 km/m"); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == "2 km/m");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("percents")
|
||||
{
|
||||
const auto q = value_cast<percent>(15. * isq::length[m] / (100. * isq::length[m]));
|
||||
constexpr auto q = value_cast<percent>(15. * isq::length[m] / (100. * isq::length[m]));
|
||||
os << q;
|
||||
|
||||
static_assert(!space_before_unit_symbol<get_unit(q.reference)>);
|
||||
|
||||
SECTION("iostream") { CHECK(os.str() == "15%"); }
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "15 %"); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("radians")
|
||||
@@ -249,7 +264,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +282,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 °"); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("arcminute")
|
||||
@@ -276,7 +297,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 ′"); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("arcsecond")
|
||||
@@ -288,7 +312,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 ″"); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +330,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("signed negative")
|
||||
@@ -315,7 +345,10 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unsigned")
|
||||
@@ -327,20 +360,23 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
|
||||
|
||||
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
|
||||
SECTION("fmt with format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U}", q) == os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("format string with only %Q should print quantity value only", "[text][fmt]")
|
||||
TEST_CASE("quantity format string with only %N should print quantity value only", "[text][fmt]")
|
||||
{
|
||||
SECTION("integral representation")
|
||||
{
|
||||
SECTION("positive value") { CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); }
|
||||
SECTION("positive value") { CHECK(MP_UNITS_STD_FMT::format("{:%N}", 123 * isq::speed[km / h]) == "123"); }
|
||||
|
||||
SECTION("negative value")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,185 +384,186 @@ TEST_CASE("format string with only %Q should print quantity value only", "[text]
|
||||
{
|
||||
SECTION("positive value")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5");
|
||||
}
|
||||
|
||||
SECTION("negative value")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.859999999999999");
|
||||
}
|
||||
|
||||
SECTION("nan")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::quiet_NaN() * isq::length[m]) == "nan");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits<double>::quiet_NaN() * isq::length[m]) == "nan");
|
||||
}
|
||||
|
||||
SECTION("inf")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::infinity() * isq::length[m]) == "inf");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits<double>::infinity() * isq::length[m]) == "inf");
|
||||
}
|
||||
|
||||
SECTION("-inf")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", -std::numeric_limits<double>::infinity() * isq::length[m]) == "-inf");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%N}", -std::numeric_limits<double>::infinity() * isq::length[m]) == "-inf");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("format string with only %q should print quantity unit symbol only", "[text][fmt]")
|
||||
TEST_CASE("quantity format string with only %U should print quantity unit symbol only", "[text][fmt]")
|
||||
{
|
||||
SECTION("Text encoding")
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::speed[km / h]) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::resistance[si::kilo<si::ohm>]) == "kΩ");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::time[us]) == "µs");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * percent) == "%");
|
||||
}
|
||||
|
||||
TEST_CASE("Unit formatting should use proper text encoding")
|
||||
{
|
||||
SECTION("Unicode text output")
|
||||
{
|
||||
SECTION("Unicode text output")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h");
|
||||
// TODO enable this when resistance is defined
|
||||
// CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
}
|
||||
|
||||
SECTION("Unicode text output is used by default")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
|
||||
// CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
}
|
||||
|
||||
SECTION("ASCII text output")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h");
|
||||
// CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2");
|
||||
}
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:U}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:U}", si::kilo<si::ohm>) == "kΩ");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:U}", us) == "µs");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:U}", m / s2) == "m/s²");
|
||||
}
|
||||
|
||||
SECTION("Solidus")
|
||||
SECTION("Unicode text output is used by default")
|
||||
{
|
||||
SECTION("Solidus for only one element in denominator")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
|
||||
SECTION("Solidus for only one element in denominator is used by default")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
|
||||
SECTION("Always use solidus")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
|
||||
}
|
||||
|
||||
SECTION("Never use solidus")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", si::kilo<si::ohm>) == "kΩ");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", us) == "µs");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²");
|
||||
}
|
||||
|
||||
SECTION("Separator")
|
||||
SECTION("ASCII text output")
|
||||
{
|
||||
SECTION("Space")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
|
||||
}
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:A}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:A}", si::kilo<si::ohm>) == "kohm");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:A}", us) == "us");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:A}", m / s2) == "m/s^2");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Space is used by default")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
|
||||
}
|
||||
TEST_CASE("Unit formatting should print solidus according to specs")
|
||||
{
|
||||
SECTION("Solidus for only one element in denominator")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:1}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:1}", m / s2) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:1}", kg / m / s2) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
|
||||
SECTION("Dot")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)");
|
||||
}
|
||||
SECTION("Solidus for only one element in denominator is used by default")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
|
||||
SECTION("Always use solidus")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:a}", km / h) == "km/h");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:a}", m / s2) == "m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)");
|
||||
}
|
||||
|
||||
SECTION("Never use solidus")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:n}", km / h) == "km h⁻¹");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:n}", m / s2) == "m s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:n}", kg / m / s2) == "kg m⁻¹ s⁻²");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit formatting should user proper separator")
|
||||
{
|
||||
SECTION("Space")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:s}", kg * m / s2) == "kg m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:s}", kg / m / s2) == "kg m⁻¹ s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:sa}", kg / m / s2) == "kg/(m s²)");
|
||||
}
|
||||
|
||||
SECTION("Space is used by default")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", kg * m / s2) == "kg m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)");
|
||||
}
|
||||
|
||||
SECTION("Dot")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:d}", kg * m / s2) == "kg⋅m/s²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:d}", kg / m / s2) == "kg⋅m⁻¹⋅s⁻²");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:ad}", kg / m / s2) == "kg/(m⋅s²)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("unknown unit modifiers should throw", "[text][fmt][exception]")
|
||||
{
|
||||
auto q = 1 * isq::length[m];
|
||||
|
||||
SECTION("only the invalid modifier")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%xq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:x}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
|
||||
}
|
||||
|
||||
SECTION("invalid modifier in the front")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%xUdaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:xUda}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
|
||||
}
|
||||
|
||||
SECTION("invalid modifier in the end")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%Udaxq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:Udax}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
|
||||
}
|
||||
|
||||
SECTION("invalid modifier in the middle")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%Udxaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:Udxa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]")
|
||||
{
|
||||
auto q = 1 * isq::length[m];
|
||||
|
||||
SECTION("text encoding")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%UdaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:UdaU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUaU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUUa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
|
||||
SECTION("solidus")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%aUdaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:aUda}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:daUa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:daaU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
|
||||
SECTION("separator")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUadq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUad}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dadUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dadU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%addUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:addU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
@@ -534,43 +571,41 @@ TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]")
|
||||
|
||||
TEST_CASE("more then one modifier of the same kind should throw", "[text][fmt][exception]")
|
||||
{
|
||||
auto q = 1 * isq::length[m];
|
||||
|
||||
SECTION("text encoding")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%UdaAq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:UdaA}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dAaU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dAUa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
|
||||
SECTION("solidus")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%aUdnq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:aUdn}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dnUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dnUa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daoUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:da1U}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
|
||||
Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
|
||||
SECTION("separator")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUasq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUas}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%sadUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:sadU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%adsUq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:adsU}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
|
||||
}
|
||||
@@ -578,23 +613,21 @@ TEST_CASE("more then one modifier of the same kind should throw", "[text][fmt][e
|
||||
|
||||
TEST_CASE("half_high_dot separator requested for ASCII encoding should throw", "[text][fmt][exception]")
|
||||
{
|
||||
auto q = 1 * isq::length[m];
|
||||
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAaq}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dAa}", MP_UNITS_STD_FMT::make_format_args(m)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("half_high_dot unit separator allowed only for Unicode encoding"));
|
||||
}
|
||||
|
||||
TEST_CASE("%q and %Q can be put anywhere in a format string", "[text][fmt]")
|
||||
TEST_CASE("%U and %N can be put anywhere in a format string", "[text][fmt]")
|
||||
{
|
||||
SECTION("no space") { CHECK(MP_UNITS_STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); }
|
||||
SECTION("no space") { CHECK(MP_UNITS_STD_FMT::format("{:%N%U}", 123 * isq::speed[km / h]) == "123km/h"); }
|
||||
|
||||
SECTION("separator") { CHECK(MP_UNITS_STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); }
|
||||
SECTION("separator") { CHECK(MP_UNITS_STD_FMT::format("{:%N###%U}", 123 * isq::speed[km / h]) == "123###km/h"); }
|
||||
|
||||
SECTION("opposite order") { CHECK(MP_UNITS_STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); }
|
||||
SECTION("opposite order") { CHECK(MP_UNITS_STD_FMT::format("{:%U %N}", 123 * isq::speed[km / h]) == "km/h 123"); }
|
||||
}
|
||||
|
||||
TEST_CASE("fill and align specification", "[text][fmt][ostream]")
|
||||
TEST_CASE("quantity fill and align specification", "[text][fmt][ostream]")
|
||||
{
|
||||
SECTION("ostream")
|
||||
{
|
||||
@@ -649,40 +682,40 @@ TEST_CASE("fill and align specification", "[text][fmt][ostream]")
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|");
|
||||
}
|
||||
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
SECTION("full format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%N%?%U}|", 123 * isq::length[m]) == "|123 m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%N%?%U}|", 123 * isq::length[m]) == "| 123 m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%N%?%U}|", 123 * isq::length[m]) == "|123 m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%N%?%U}|", 123 * isq::length[m]) == "| 123 m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%N%?%U}|", 123 * isq::length[m]) == "| 123 m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%N%?%U}|", 123 * isq::length[m]) == "|123 m*****|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%N%?%U}|", 123 * isq::length[m]) == "|*****123 m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%N%?%U}|", 123 * isq::length[m]) == "|**123 m***|");
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
SECTION("value only format {:%N} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%N}|", 123 * isq::length[m]) == "|123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%N}|", 123 * isq::length[m]) == "| 123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%N}|", 123 * isq::length[m]) == "|123 |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%N}|", 123 * isq::length[m]) == "| 123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%N}|", 123 * isq::length[m]) == "| 123 |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%N}|", 123 * isq::length[m]) == "|123*******|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%N}|", 123 * isq::length[m]) == "|*******123|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%N}|", 123 * isq::length[m]) == "|***123****|");
|
||||
}
|
||||
|
||||
SECTION("symbol only format {:%q} on a quantity")
|
||||
SECTION("symbol only format {:%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:0%U}|", 123 * isq::length[m]) == "|m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:10%U}|", 123 * isq::length[m]) == "| m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:<10%U}|", 123 * isq::length[m]) == "|m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:>10%U}|", 123 * isq::length[m]) == "| m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:^10%U}|", 123 * isq::length[m]) == "| m |");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%U}|", 123 * isq::length[m]) == "|m*********|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%U}|", 123 * isq::length[m]) == "|*********m|");
|
||||
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%U}|", 123 * isq::length[m]) == "|****m*****|");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,142 +724,139 @@ TEST_CASE("sign specification", "[text][fmt]")
|
||||
auto inf = std::numeric_limits<double>::infinity() * si::metre;
|
||||
auto nan = std::numeric_limits<double>::quiet_NaN() * si::metre;
|
||||
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
SECTION("full format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:{%N:+}%U},{0:{%N:-}%U},{0:{%N: }%U}", 1 * isq::length[m]) ==
|
||||
"1m,+1m,1m, 1m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:{%N:+}%U},{0:{%N:-}%U},{0:{%N: }%U}", -1 * isq::length[m]) ==
|
||||
"-1m,-1m,-1m,-1m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:{%N:+}%U},{0:{%N:-}%U},{0:{%N: }%U}", inf) == "infm,+infm,infm, infm");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:{%N:+}%U},{0:{%N:-}%U},{0:{%N: }%U}", nan) == "nanm,+nanm,nanm, nanm");
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
SECTION("value only format {:%N} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:{%N:+}},{0:{%N:-}},{0:{%N: }}", 1 * isq::length[m]) == "1,+1,1, 1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:{%N:+}},{0:{%N:-}},{0:{%N: }}", -1 * isq::length[m]) == "-1,-1,-1,-1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:{%N:+}},{0:{%N:-}},{0:{%N: }}", inf) == "inf,+inf,inf, inf");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:{%N:+}},{0:{%N:-}},{0:{%N: }}", nan) == "nan,+nan,nan, nan");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("precision specification", "[text][fmt]")
|
||||
{
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
SECTION("full format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.0f}%?%U}", 1.2345 * isq::length[m]) == "1 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.1f}%?%U}", 1.2345 * isq::length[m]) == "1.2 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.2f}%?%U}", 1.2345 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3f}%?%U}", 1.2345 * isq::length[m]) == "1.234 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.4f}%?%U}", 1.2345 * isq::length[m]) == "1.2345 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.5f}%?%U}", 1.2345 * isq::length[m]) == "1.23450 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.10f}%?%U}", 1.2345 * isq::length[m]) == "1.2345000000 m");
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
SECTION("value only format {:%N} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("precision specification for integral representation should throw", "[text][fmt][exception]")
|
||||
{
|
||||
auto q = 1 * isq::length[m];
|
||||
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%.1Q %q}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%.1Q}", MP_UNITS_STD_FMT::make_format_args(q)),
|
||||
MP_UNITS_STD_FMT::format_error,
|
||||
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.0f}}", 1.2345 * isq::length[m]) == "1");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.1f}}", 1.2345 * isq::length[m]) == "1.2");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.2f}}", 1.2345 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3f}}", 1.2345 * isq::length[m]) == "1.234");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.4f}}", 1.2345 * isq::length[m]) == "1.2345");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.5f}}", 1.2345 * isq::length[m]) == "1.23450");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.10f}}", 1.2345 * isq::length[m]) == "1.2345000000");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("type specification", "[text][fmt]")
|
||||
{
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
SECTION("full format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:b}%?%U}", 42 * isq::length[m]) == "101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:B}%?%U}", 42 * isq::length[m]) == "101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:d}%?%U}", 42 * isq::length[m]) == "42 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:o}%?%U}", 42 * isq::length[m]) == "52 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:x}%?%U}", 42 * isq::length[m]) == "2a m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:X}%?%U}", 42 * isq::length[m]) == "2A m");
|
||||
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:a}%?%U}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3a}%?%U}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:A}%?%U}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3A}%?%U}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m");
|
||||
#else
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:a}%?%U}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3a}%?%U}", 1.2345678 * isq::length[m]) == "1.3c1p+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:A}%?%U}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3A}%?%U}", 1.2345678 * isq::length[m]) == "1.3C1P+0 m");
|
||||
#endif
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:e}%?%U}", 1.2345678 * isq::length[m]) == "1.234568e+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3e}%?%U}", 1.2345678 * isq::length[m]) == "1.235e+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:E}%?%U}", 1.2345678 * isq::length[m]) == "1.234568E+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3E}%?%U}", 1.2345678 * isq::length[m]) == "1.235E+00 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:g}%?%U}", 1.2345678 * isq::length[m]) == "1.23457 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:g}%?%U}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3g}%?%U}", 1.2345678 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3g}%?%U}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:G}%?%U}", 1.2345678 * isq::length[m]) == "1.23457 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:G}%?%U}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3G}%?%U}", 1.2345678 * isq::length[m]) == "1.23 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3G}%?%U}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
SECTION("value only format {:%N} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:b}}", 42 * isq::length[m]) == "101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:B}}", 42 * isq::length[m]) == "101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:d}}", 42 * isq::length[m]) == "42");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:o}}", 42 * isq::length[m]) == "52");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:x}}", 42 * isq::length[m]) == "2a");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:X}}", 42 * isq::length[m]) == "2A");
|
||||
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08");
|
||||
#if MP_UNITS_USE_FMTLIB
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:a}}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3a}}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:A}}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3A}}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0");
|
||||
#else
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:a}}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3a}}", 1.2345678 * isq::length[m]) == "1.3c1p+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:A}}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3A}}", 1.2345678 * isq::length[m]) == "1.3C1P+0");
|
||||
#endif
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:e}}", 1.2345678 * isq::length[m]) == "1.234568e+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3e}}", 1.2345678 * isq::length[m]) == "1.235e+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:E}}", 1.2345678 * isq::length[m]) == "1.234568E+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3E}}", 1.2345678 * isq::length[m]) == "1.235E+00");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:g}}", 1.2345678 * isq::length[m]) == "1.23457");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:g}}", 1.2345678e8 * isq::length[m]) == "1.23457e+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3g}}", 1.2345678 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3g}}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:G}}", 1.2345678 * isq::length[m]) == "1.23457");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:G}}", 1.2345678e8 * isq::length[m]) == "1.23457E+08");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3G}}", 1.2345678 * isq::length[m]) == "1.23");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:.3G}}", 1.2345678e8 * isq::length[m]) == "1.23E+08");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("different base types with the # specifier", "[text][fmt]")
|
||||
{
|
||||
SECTION("full format {:%Q %q} on a quantity")
|
||||
SECTION("full format {:%N%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#b}%?%U}", 42 * isq::length[m]) == "0b101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#B}%?%U}", 42 * isq::length[m]) == "0B101010 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#o}%?%U}", 42 * isq::length[m]) == "052 m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#x}%?%U}", 42 * isq::length[m]) == "0x2a m");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#X}%?%U}", 42 * isq::length[m]) == "0X2A m");
|
||||
}
|
||||
|
||||
SECTION("value only format {:%Q} on a quantity")
|
||||
SECTION("value only format {:%N} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#b}}", 42 * isq::length[m]) == "0b101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#B}}", 42 * isq::length[m]) == "0B101010");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#o}}", 42 * isq::length[m]) == "052");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#x}}", 42 * isq::length[m]) == "0x2a");
|
||||
CHECK(MP_UNITS_STD_FMT::format("{:{%N:#X}}", 42 * isq::length[m]) == "0X2A");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,10 +875,10 @@ TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]")
|
||||
std::locale grp2{std::locale::classic(), new group2};
|
||||
std::locale grp3{std::locale::classic(), new group3};
|
||||
|
||||
SECTION("full format {:%LQ %q} on a quantity")
|
||||
SECTION("full format {:{%N:L}%?%U} on a quantity")
|
||||
{
|
||||
CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%LQ %q}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s");
|
||||
CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%LQ %q}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s");
|
||||
CHECK(MP_UNITS_STD_FMT::format(grp2, "{:{%N:L}%?%U}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s");
|
||||
CHECK(MP_UNITS_STD_FMT::format(grp3, "{:{%N:L}%?%U}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -25,7 +25,6 @@ import os
|
||||
from conan import ConanFile
|
||||
from conan.tools.build import can_run
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
from conan.tools.scm import Version
|
||||
|
||||
|
||||
class TestPackageConan(ConanFile):
|
||||
|
Reference in New Issue
Block a user