From 9b2f9a7a36dfce6e09d287981141c69bad704baa Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Mon, 2 Dec 2024 11:03:56 -0500 Subject: [PATCH] Add machinery for built-in constants, such as `c` (#335) For Au's built-in constants, we follow the exact same policies as for units, including: - A new include folder, `"au/constants/..."` - A new target, `"//au:constants"`, which globs headers from that folder - Corresponding unit tests - Inclusion in the single-file script by individual names - An `--all-constants` option for the single-file script Oh, and while I was updating the single-file script, I noticed a slight "bug": ever since we started providing `_fwd.hh` files for the units, the single file script was treating those files as their own units. This doesn't _hurt_ anything, but it's just a little silly (see image). This PR fixes that bug as well. ![image](https://github.com/user-attachments/assets/22381e3e-4785-4c69-8ae9-94e4ff7c7c41) We _don't_ provide `_fwd.hh` files for _constants_, because there's nothing we could really forward declare. Constant objects are defined with spelled-out names in `ALL_CAPS` format. The corresponding file is the snake-case version. This keeps the constant itself unambiguous. We expect end users to actually use them in the following manner: ```cpp constexpr auto c = au::SPEED_OF_LIGHT; ``` Finally, we now mention new constants in the release notes. Helps #90. Remaining work includes adding more constants, and adding documentation. --- BUILD.bazel | 6 +- RELEASE.md | 6 +- au/BUILD.bazel | 23 +++++++ au/code/au/CMakeLists.txt | 10 +++ au/code/au/constant.hh | 1 - au/code/au/constants/speed_of_light.hh | 38 +++++++++++ .../au/constants/test/speed_of_light_test.cc | 36 ++++++++++ tools/bin/make-single-file | 66 ++++++++++++++----- 8 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 au/code/au/constants/speed_of_light.hh create mode 100644 au/code/au/constants/test/speed_of_light_test.cc diff --git a/BUILD.bazel b/BUILD.bazel index ca329bbc..905b690a 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -76,7 +76,7 @@ BASE_UNIT_STRING = " ".join(BASE_UNITS) GIT_ID_CMD = "cat bazel-out/stable-status.txt | grep STABLE_GIT_ID | sed 's/STABLE_GIT_ID \\(.*\\)/\\1/' | tr -d '\\n'" -CMD_ROOT = "$(location tools/bin/make-single-file) {extra_opts} {units} --version-id $$({id_cmd}) > $(OUTS)" +CMD_ROOT = "$(location tools/bin/make-single-file) {extra_opts} {units} {all_constants} --version-id $$({id_cmd}) > $(OUTS)" ################################################################################ # Release single-file package `au.hh` @@ -86,6 +86,7 @@ genrule( srcs = ["//au:headers"], outs = ["docs/au.hh"], cmd = CMD_ROOT.format( + all_constants = "", extra_opts = "", id_cmd = GIT_ID_CMD, units = "--units " + BASE_UNIT_STRING, @@ -108,6 +109,7 @@ genrule( srcs = ["//au:headers"], outs = ["docs/au_noio.hh"], cmd = CMD_ROOT.format( + all_constants = "", extra_opts = "--noio", id_cmd = GIT_ID_CMD, units = "--units " + BASE_UNIT_STRING, @@ -131,6 +133,7 @@ genrule( srcs = ["//au:headers"], outs = ["docs/au_all_units.hh"], cmd = CMD_ROOT.format( + all_constants = "--all-constants", extra_opts = "", id_cmd = GIT_ID_CMD, units = "--all-units", @@ -153,6 +156,7 @@ genrule( srcs = ["//au:headers"], outs = ["docs/au_all_units_noio.hh"], cmd = CMD_ROOT.format( + all_constants = "--all-constants", extra_opts = "--noio", id_cmd = GIT_ID_CMD, units = "--all-units", diff --git a/RELEASE.md b/RELEASE.md index 0d6ffc05..7a9d0de6 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -38,7 +38,7 @@ keep track of the main changes as you go. Use the following categories. - User-facing library changes - If the compilation speed has been significantly impacted, mention this here. -- New units +- New units and constants - Tooling updates - Documentation updates - Repo updates @@ -78,8 +78,8 @@ Release Notes User-facing library changes --------------------------- -New units ---------- +New units and constants +----------------------- Tooling updates --------------- diff --git a/au/BUILD.bazel b/au/BUILD.bazel index 826f55b8..27a89d48 100644 --- a/au/BUILD.bazel +++ b/au/BUILD.bazel @@ -40,6 +40,29 @@ cc_test( ], ) +cc_library( + name = "constants", + hdrs = glob(["code/au/constants/*.hh"]), + includes = ["code"], + visibility = ["//visibility:public"], + deps = [ + ":constant", + ":units", + ], +) + +cc_test( + name = "constants_test", + size = "small", + srcs = glob(["code/au/constants/test/*.cc"]), + deps = [ + ":constants", + ":testing", + ":units", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "fwd", hdrs = ["code/au/fwd.hh"], diff --git a/au/code/au/CMakeLists.txt b/au/code/au/CMakeLists.txt index 4a9d7b40..a182fd30 100644 --- a/au/code/au/CMakeLists.txt +++ b/au/code/au/CMakeLists.txt @@ -43,6 +43,7 @@ header_only_library( unit_symbol.hh wrapper_operations.hh zero.hh + constants/speed_of_light.hh stdx/experimental/is_detected.hh stdx/functional.hh stdx/type_traits.hh @@ -220,6 +221,15 @@ gtest_based_test( testing ) +gtest_based_test( + NAME constants_test + SRCS + constants/test/speed_of_light_test.cc + DEPS + au + testing +) + gtest_based_test( NAME io_test SRCS diff --git a/au/code/au/constant.hh b/au/code/au/constant.hh index b41b5155..a84dc375 100644 --- a/au/code/au/constant.hh +++ b/au/code/au/constant.hh @@ -16,7 +16,6 @@ #include "au/fwd.hh" #include "au/quantity.hh" -#include "au/quantity_point.hh" #include "au/stdx/type_traits.hh" #include "au/unit_of_measure.hh" #include "au/wrapper_operations.hh" diff --git a/au/code/au/constants/speed_of_light.hh b/au/code/au/constants/speed_of_light.hh new file mode 100644 index 00000000..b7c2660f --- /dev/null +++ b/au/code/au/constants/speed_of_light.hh @@ -0,0 +1,38 @@ +// Copyright 2024 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. + +#include "au/constant.hh" +#include "au/units/meters.hh" +#include "au/units/seconds.hh" + +namespace au { + +namespace detail { +// DO NOT follow this pattern to define your own units. This is for library-defined units. +// Instead, follow instructions at (https://aurora-opensource.github.io/au/main/howto/new-units/). +template +struct SpeedOfLightLabel { + static constexpr const char label[] = "c"; +}; +template +constexpr const char SpeedOfLightLabel::label[]; +struct SpeedOfLightUnit : decltype(Meters{} / Seconds{} * mag<299'792'458>()), + SpeedOfLightLabel { + using SpeedOfLightLabel::label; +}; +} // namespace detail + +constexpr auto SPEED_OF_LIGHT = make_constant(detail::SpeedOfLightUnit{}); + +} // namespace au diff --git a/au/code/au/constants/test/speed_of_light_test.cc b/au/code/au/constants/test/speed_of_light_test.cc new file mode 100644 index 00000000..512e23f6 --- /dev/null +++ b/au/code/au/constants/test/speed_of_light_test.cc @@ -0,0 +1,36 @@ +// Copyright 2024 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. + +#include "au/constants/speed_of_light.hh" + +#include "au/testing.hh" +#include "au/units/meters.hh" +#include "au/units/seconds.hh" +#include "gtest/gtest.h" + +namespace au { +namespace { + +using symbols::m; +using symbols::s; +using ::testing::StrEq; + +TEST(SpeedOfLight, HasExpectedValue) { + EXPECT_THAT(SPEED_OF_LIGHT.as(m / s), SameTypeAndValue(299'792'458 * m / s)); +} + +TEST(SpeedOfLight, HasExpectedLabel) { EXPECT_THAT(unit_label(SPEED_OF_LIGHT), StrEq("c")); } + +} // namespace +} // namespace au diff --git a/tools/bin/make-single-file b/tools/bin/make-single-file index f9a0d78e..171a73a7 100755 --- a/tools/bin/make-single-file +++ b/tools/bin/make-single-file @@ -46,10 +46,13 @@ def main(argv=None): transitively included files which are within the project, but we leave other `#include` directives (such as standard library headers) untouched. """ - args = enumerate_units(parse_command_line_args(argv)) + args = enumerate_units_and_constants(parse_command_line_args(argv)) files = parse_files( filenames=filenames( - main_files=args.main_files, units=args.units, include_io=args.include_io + main_files=args.main_files, + units=args.units, + constants=args.constants, + include_io=args.include_io, ) ) print_unified_file(files, args=args) @@ -57,13 +60,18 @@ def main(argv=None): return 0 -def filenames(main_files, units, include_io): +def filenames(main_files, units, constants, include_io): """Construct the list of project filenames to include. The script will be sure to include all of these, and will also include any transitive dependencies from within the project. """ - names = ["au/au.hh"] + [f"au/units/{unit}.hh" for unit in units] + main_files + names = ( + ["au/au.hh"] + + [f"au/units/{u}.hh" for u in units] + + [f"au/constants/{c.lower()}.hh" for c in constants] + + main_files + ) if include_io: names.append("au/io.hh") return names @@ -85,6 +93,16 @@ def parse_command_line_args(argv): help="Include all units (may slow compilation!)", ) + constant_group = parser.add_mutually_exclusive_group(required=False) + constant_group.add_argument( + "--constants", nargs="*", default=[], help="The constants to include" + ) + constant_group.add_argument( + "--all-constants", + action="store_true", + help="Include all constants (may slow compilation!)", + ) + parser.add_argument( "--version-id", default=git_id_description(), @@ -101,17 +119,30 @@ def parse_command_line_args(argv): return parser.parse_args() -def enumerate_units(args): +def enumerate_units_and_constants(args): """ - Massage args object so that it's "as if" user had specified all units manually. + Massage args object so that it's "as if" user had specified all units/constants manually. - This means that if `--all-units` is specified, we populate the `units` list - with every existing entry, and then delete `--all-units`. + This means that if `--all-units` is specified, we populate the `units` list with every existing + entry, and then delete `--all-units`, and similarly for `--all-constants`. """ - if args.all_units: - args.units = [f[:-3] for f in os.listdir("au/code/au/units/") if f.endswith(".hh")] + def looks_like_header(f): + return f.endswith(".hh") and not f.endswith("_fwd.hh") + if args.all_units: + args.units = [ + f[:-3] for f in os.listdir("au/code/au/units/") if looks_like_header(f) + ] del args.all_units + + if args.all_constants: + args.constants = [ + f[:-3].upper() + for f in os.listdir("au/code/au/constants/") + if looks_like_header(f) + ] + del args.all_constants + return args @@ -247,11 +278,16 @@ def manifest(args): """A sequence of lines describing the options that generated this file.""" args = CheckArgs(args) - lines = [ - f"Version identifier: {args.version_id}", - f' support: {"INCLUDED" if args.include_io else "EXCLUDED"}', - "List of included units:", - ] + [f" {u}" for u in sorted(args.units)] + lines = ( + [ + f"Version identifier: {args.version_id}", + f' support: {"INCLUDED" if args.include_io else "EXCLUDED"}', + "List of included units:", + ] + + [f" {u}" for u in sorted(args.units)] + + ["List of included constants:"] + + [f" {c}" for c in sorted(args.constants)] + ) if args.main_files: lines.append("Extra files included:")