Skip to content

Commit

Permalink
Merge branch 'main' into quantity-point#140
Browse files Browse the repository at this point in the history
  • Loading branch information
chiphogg committed Aug 29, 2023
2 parents 8a8dd92 + 28a72f8 commit 7618738
Show file tree
Hide file tree
Showing 21 changed files with 695 additions and 145 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ jobs:
- uses: actions/checkout@dc323e67f16fb5f7663d20ff7941f27f5809e9b6 #v2.6.0
- name: Build and test (${{ inputs.config }})
run: bazel test --copt=-Werror --config=${{ inputs.config }} //...:all
- name: Build and test with -Wconversion (${{ inputs.config }})
run: bazel test --copt=-Werror --copt=-Wconversion --config=${{ inputs.config }} `bazel query 'kind(".*_test", //au/...:all except //au:no_wconversion_test)'`
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,29 @@ maximize the chance of delivering its value!

See our [development setup guide](https://aurora-opensource.github.io/au/develop/) to get started
with building and testing the code and documentation!

### Style guide

Au follows the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), apart
from the explicit exceptions we enumerate below. Please make sure your PR conforms to this modified
style guide before landing.

Here are the modifications we've made to the Google C++ Style Guide for Au:

1. The `.clang-format` file in the repository supersedes the Google style guide in all matters of
code formatting.

2. Our header files use the `.hh` extension, not
[`.h`](https://google.github.io/styleguide/cppguide.html#Header_Files).

3. Use `#pragma once` instead of [`#define`
guards](https://google.github.io/styleguide/cppguide.html#The__define_Guard).

4. We permit [implicit
conversions](https://google.github.io/styleguide/cppguide.html#Implicit_Conversions) that perform
unit conversions we expect to be safe.

5. We do not use the [`kConstantName`
convention](https://google.github.io/styleguide/cppguide.html#Constant_Names) for our constants.
Instead, we use the `UPPER_SNAKE_CASE` convention. This also applies to [enumerator
names](https://google.github.io/styleguide/cppguide.html#Enumerator_Names).
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
![Au library logo](docs/assets/au-logo-color.png)

[![clang14-ubuntu](
https://github.com/aurora-opensource/au/actions/workflows/clang14-ubuntu.yml/badge.svg?branch=main&event=push)](
https://github.com/aurora-opensource/au/actions/workflows/clang14-ubuntu.yml) [![clang11-ubuntu](
https://github.com/aurora-opensource/au/actions/workflows/clang11-ubuntu.yml/badge.svg?branch=main&event=push)](
https://github.com/aurora-opensource/au/actions/workflows/clang11-ubuntu.yml) [![gcc10-ubuntu](
https://github.com/aurora-opensource/au/actions/workflows/gcc10-ubuntu.yml/badge.svg?branch=main&event=push)](
https://github.com/aurora-opensource/au/actions/workflows/gcc10-ubuntu.yml) [![MSVC x64 19.29](
https://github.com/aurora-opensource/au/actions/workflows/msvc-x64-19-29-30151.yml/badge.svg?branch=main&event=push)](
https://github.com/aurora-opensource/au/actions/workflows/msvc-x64-19-29-30151.yml) [![MSVC x64 19.35](
https://github.com/aurora-opensource/au/actions/workflows/msvc-x64-19-35-32217-1.yml/badge.svg?branch=main&event=push)](
https://github.com/aurora-opensource/au/actions/workflows/msvc-x64-19-35-32217-1.yml)

# Au: A C++14-compatible units library, by Aurora

Au (pronounced "ay yoo") is a C++ units library, by [Aurora](https://aurora.tech/). What the
Expand Down
15 changes: 15 additions & 0 deletions au/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ cc_test(
srcs = ["dimension_test.cc"],
deps = [
":dimension",
":testing",
":units",
"@com_google_googletest//:gtest_main",
],
)
Expand Down Expand Up @@ -230,6 +232,18 @@ cc_test(
],
)

cc_test(
name = "no_wconversion_test",
size = "small",
srcs = ["no_wconversion_test.cc"],
deps = [
":quantity",
":testing",
":units",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "packs",
hdrs = ["packs.hh"],
Expand Down Expand Up @@ -378,6 +392,7 @@ cc_test(
":prefix",
":testing",
":unit_of_measure",
":units",
"@com_google_googletest//:gtest_main",
],
)
Expand Down
26 changes: 13 additions & 13 deletions au/dimension.hh
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,25 @@ struct CommonDimension<Head, Tail...> : CommonDimension<Tail...> {

namespace base_dim {

template <int I>
template <int64_t I>
struct BaseDimension {
static constexpr int base_dim_index = I;
static constexpr int64_t base_dim_index = I;
};
template <int I>
constexpr int BaseDimension<I>::base_dim_index;
template <int64_t I>
constexpr int64_t BaseDimension<I>::base_dim_index;

template <typename T, typename U>
struct OrderByBaseDimIndex : stdx::bool_constant<(T::base_dim_index < U::base_dim_index)> {};

struct Length : BaseDimension<1> {};
struct Mass : BaseDimension<2> {};
struct Time : BaseDimension<3> {};
struct Current : BaseDimension<4> {};
struct Temperature : BaseDimension<5> {};
struct Angle : BaseDimension<6> {};
struct Information : BaseDimension<7> {};
struct AmountOfSubstance : BaseDimension<8> {};
struct LuminousIntensity : BaseDimension<9> {};
struct Length : BaseDimension<-99> {};
struct Mass : BaseDimension<-98> {};
struct Time : BaseDimension<-97> {};
struct Current : BaseDimension<-96> {};
struct Temperature : BaseDimension<-95> {};
struct Angle : BaseDimension<-94> {};
struct Information : BaseDimension<-93> {};
struct AmountOfSubstance : BaseDimension<-92> {};
struct LuminousIntensity : BaseDimension<-91> {};

} // namespace base_dim

Expand Down
15 changes: 15 additions & 0 deletions au/dimension_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include "au/dimension.hh"

#include "au/testing.hh"
#include "au/units/inches.hh"
#include "gtest/gtest.h"

using ::testing::StaticAssertTypeEq;
Expand All @@ -23,6 +25,19 @@ namespace au {
using Speed = DimQuotientT<Length, Time>;
using Accel = DimQuotientT<Speed, Time>;

// Test code for a new user-defined dimension.
struct PixelBaseDim : base_dim::BaseDimension<1690384951> {};
struct Pixels : UnitImpl<Dimension<PixelBaseDim>> {
static constexpr const char label[] = "px";
};
constexpr const char Pixels::label[];
constexpr auto pixels = QuantityMaker<Pixels>{};

TEST(Dimension, CanDefineNewBaseDimension) {
constexpr auto resolution = (pixels / inch)(300);
EXPECT_THAT(resolution * inches(6), SameTypeAndValue(pixels(1800)));
}

TEST(Dimension, AllProvidedBaseDimensionsAreCompatible) {
// This tests the strict total ordering for all recognized base dimensions. It makes sure they
// are all distinguishable and orderable, and thus can be combined in a single dimension.
Expand Down
4 changes: 2 additions & 2 deletions au/magnitude.hh
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,10 @@ using Widen = std::conditional_t<
std::conditional_t<std::is_signed<T>::value, std::intmax_t, std::uintmax_t>>,
T>;

template <typename T, int N, typename B>
template <typename T, std::intmax_t N, typename B>
constexpr Widen<T> base_power_value(B base) {
return (N < 0) ? (Widen<T>{1} / base_power_value<T, -N>(base))
: int_pow(static_cast<Widen<T>>(base), N);
: int_pow(static_cast<Widen<T>>(base), static_cast<std::uintmax_t>(N));
}

template <typename T, std::size_t N>
Expand Down
20 changes: 20 additions & 0 deletions au/math.hh
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ auto arctan2(Quantity<U1, R1> y, Quantity<U2, R2> x) {
return arctan2(y.in(common_unit), x.in(common_unit));
}

// Clamp the first quantity to within the range of the second two.
template <typename UV, typename ULo, typename UHi, typename RV, typename RLo, typename RHi>
constexpr auto clamp(Quantity<UV, RV> v, Quantity<ULo, RLo> lo, Quantity<UHi, RHi> hi) {
using U = CommonUnitT<UV, ULo, UHi>;
using R = std::common_type_t<RV, RLo, RHi>;
using ResultT = Quantity<U, R>;
return (v < lo) ? ResultT{lo} : (hi < v) ? ResultT{hi} : ResultT{v};
}

// Clamp the first point to within the range of the second two.
template <typename UV, typename ULo, typename UHi, typename RV, typename RLo, typename RHi>
constexpr auto clamp(QuantityPoint<UV, RV> v,
QuantityPoint<ULo, RLo> lo,
QuantityPoint<UHi, RHi> hi) {
using U = CommonPointUnitT<UV, ULo, UHi>;
using R = std::common_type_t<RV, RLo, RHi>;
using ResultT = QuantityPoint<U, R>;
return (v < lo) ? ResultT{lo} : (hi < v) ? ResultT{hi} : ResultT{v};
}

// Copysign where the magnitude has units.
template <typename U, typename R, typename T>
constexpr auto copysign(Quantity<U, R> mag, T sgn) {
Expand Down
80 changes: 79 additions & 1 deletion au/math_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "au/units/fahrenheit.hh"
#include "au/units/hertz.hh"
#include "au/units/inches.hh"
#include "au/units/kelvins.hh"
#include "au/units/meters.hh"
#include "au/units/ohms.hh"
#include "au/units/seconds.hh"
Expand All @@ -32,6 +33,18 @@ using ::testing::StaticAssertTypeEq;
namespace au {
namespace {
constexpr auto INTEGER_TOO_BIG_FOR_DOUBLE = 9'007'199'254'740'993LL;

// Backport of std::clamp() from C++17 for testing purposes.
//
// See for implementation: https://en.cppreference.com/w/cpp/algorithm/clamp
template <class T, class Compare>
constexpr const T &std_clamp(const T &v, const T &lo, const T &hi, Compare comp) {
return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
}
template <class T>
constexpr const T &std_clamp(const T &v, const T &lo, const T &hi) {
return std_clamp(v, lo, hi, std::less<void>{});
}
} // namespace

TEST(abs, AlwaysReturnsNonnegativeVersionOfInput) {
Expand All @@ -55,10 +68,75 @@ TEST(abs, SameAsStdAbsForNumericTypes) {
EXPECT_EQ(abs(1), 1);
}

TEST(clamp, QuantityConsistentWithStdClampWhenTypesAreIdentical) {
auto expect_consistent_with_std_clamp = [](auto v, auto lo, auto hi) {
const auto expected = ohms(std_clamp(v, lo, hi));
const auto actual = clamp(ohms(v), ohms(lo), ohms(hi));
EXPECT_THAT(actual, SameTypeAndValue(expected));
};

// Rep: `int`.
expect_consistent_with_std_clamp(-1, 0, 1);
expect_consistent_with_std_clamp(0, 0, 1);
expect_consistent_with_std_clamp(1, 0, 1);
expect_consistent_with_std_clamp(2, 0, 1);

// Rep: `double`.
expect_consistent_with_std_clamp(-1.0, 0.0, 1.0);
expect_consistent_with_std_clamp(0.0, 0.0, 1.0);
expect_consistent_with_std_clamp(1.0, 0.0, 1.0);
expect_consistent_with_std_clamp(2.0, 0.0, 1.0);
}

TEST(clamp, QuantityProducesResultsInCommonUnitOfInputs) {
EXPECT_THAT(clamp(kilo(meters)(2), milli(meters)(999), meters(20)),
SameTypeAndValue(milli(meters)(20'000)));

EXPECT_THAT(clamp(kilo(meters)(2), meters(999), meters(2'999)),
SameTypeAndValue(meters(2'000)));
}

TEST(clamp, QuantityPointConsistentWithStdClampWhenTypesAreIdentical) {
auto expect_consistent_with_std_clamp = [](auto v, auto lo, auto hi) {
const auto expected = meters_pt(std_clamp(v, lo, hi));
const auto actual = clamp(meters_pt(v), meters_pt(lo), meters_pt(hi));
EXPECT_THAT(actual, SameTypeAndValue(expected));
};

// Rep: `int`.
expect_consistent_with_std_clamp(-1, 0, 1);
expect_consistent_with_std_clamp(0, 0, 1);
expect_consistent_with_std_clamp(1, 0, 1);
expect_consistent_with_std_clamp(2, 0, 1);

// Rep: `double`.
expect_consistent_with_std_clamp(-1.0, 0.0, 1.0);
expect_consistent_with_std_clamp(0.0, 0.0, 1.0);
expect_consistent_with_std_clamp(1.0, 0.0, 1.0);
expect_consistent_with_std_clamp(2.0, 0.0, 1.0);
}

TEST(clamp, QuantityPointProducesResultsInCommonUnitOfInputs) {
EXPECT_THAT(clamp(kilo(meters_pt)(2), milli(meters_pt)(999), meters_pt(20)),
SameTypeAndValue(milli(meters_pt)(20'000)));

EXPECT_THAT(clamp(kilo(meters_pt)(2), meters_pt(999), meters_pt(2'999)),
SameTypeAndValue(meters_pt(2'000)));
}

TEST(clamp, QuantityPointTakesOffsetIntoAccount) {
// Recall that 0 degrees Celsius is 273.15 Kelvins. We know that `clamp` must take the origin
// into account for this mixed result. This means whatever unit we return must be at most 1/20
// Kelvins, and must evenly divide 1/20 Kelvins.
constexpr auto celsius_origin = clamp(celsius_pt(0), kelvins_pt(200), kelvins_pt(300));
ASSERT_TRUE(is_integer(unit_ratio(Kelvins{} / mag<20>(), decltype(celsius_origin)::unit)));
EXPECT_EQ(celsius_origin, centi(kelvins_pt)(273'15));
}

TEST(copysign, ReturnsSameTypesAsStdCopysignForSameUnitInputs) {
auto expect_consistent_with_std_copysign = [](auto mag, auto raw_sgn) {
for (const auto test_sgn : {-1, 0, +1}) {
const auto sgn = test_sgn * raw_sgn;
const auto sgn = static_cast<decltype(raw_sgn)>(test_sgn) * raw_sgn;

EXPECT_THAT(copysign(mag, sgn), SameTypeAndValue(std::copysign(mag, sgn)));

Expand Down
Loading

0 comments on commit 7618738

Please sign in to comment.