diff --git a/au/BUILD.bazel b/au/BUILD.bazel index 1d7dbc85..0151ef84 100644 --- a/au/BUILD.bazel +++ b/au/BUILD.bazel @@ -432,6 +432,27 @@ cc_test( ], ) +cc_library( + name = "wrapper_operations", + hdrs = ["wrapper_operations.hh"], + deps = [ + ":quantity", + ":stdx", + ], +) + +cc_test( + name = "wrapper_operations_test", + size = "small", + srcs = ["wrapper_operations_test.cc"], + deps = [ + ":testing", + ":units", + ":wrapper_operations", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "zero", hdrs = ["zero.hh"], diff --git a/au/wrapper_operations.hh b/au/wrapper_operations.hh new file mode 100644 index 00000000..b6b2c472 --- /dev/null +++ b/au/wrapper_operations.hh @@ -0,0 +1,246 @@ +// Copyright 2023 Aurora Operations, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "au/quantity.hh" +#include "au/stdx/type_traits.hh" + +// "Mixin" classes to add operations for a "unit wrapper" --- that is, a template with a _single +// template parameter_ that is a unit. +// +// The operations are multiplication and division. The mixins will specify what types the wrapper +// can combine with in this way, and what the resulting type will be. They also take care of +// getting the resulting unit correct. Finally, they handle integer division carefully. +// +// Every mixin has at least two template parameters. +// +// 1. The unit wrapper (a template template parameter). +// 2. The specific unit that it's wrapping (for convenience in the implementation). +// +// For mixins that compose with something that is _not_ a unit wrapper --- e.g., a raw number, or a +// magnitude --- this is all they need. Other mixins compose with _other unit wrappers_, and these +// take two more template parameters: the wrapper we're composing with, and the resulting wrapper. + +namespace au { +namespace detail { + +// A SFINAE helper that is the identity, but only if we think a type is a valid rep. +// +// For now, we are restricting this to arithmetic types. This doesn't mean they're the only reps we +// support; it just means they're the only reps we can _construct via this method_. Later on, we +// would like to have a well-defined concept that defines what is and is not an acceptable rep for +// our `Quantity`. Once we have that, we can simply constrain on that concept. For more on this +// idea, see: https://github.com/aurora-opensource/au/issues/52 +struct NoTypeMember {}; +template +struct TypeIdentityIfLooksLikeValidRep + : std::conditional_t::value, stdx::type_identity, NoTypeMember> {}; +template +using TypeIdentityIfLooksLikeValidRepT = typename TypeIdentityIfLooksLikeValidRep::type; + +// +// A mixin that enables turning a raw number into a Quantity by multiplying or dividing. +// +template