Skip to content

Commit

Permalink
Add machinery for built-in constants, such as c (#335)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chiphogg authored Dec 2, 2024
1 parent d450960 commit 9b2f9a7
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 20 deletions.
6 changes: 5 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -78,8 +78,8 @@ Release Notes
User-facing library changes
---------------------------
New units
---------
New units and constants
-----------------------
Tooling updates
---------------
Expand Down
23 changes: 23 additions & 0 deletions au/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
10 changes: 10 additions & 0 deletions au/code/au/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion au/code/au/constant.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
38 changes: 38 additions & 0 deletions au/code/au/constants/speed_of_light.hh
Original file line number Diff line number Diff line change
@@ -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 <typename T>
struct SpeedOfLightLabel {
static constexpr const char label[] = "c";
};
template <typename T>
constexpr const char SpeedOfLightLabel<T>::label[];
struct SpeedOfLightUnit : decltype(Meters{} / Seconds{} * mag<299'792'458>()),
SpeedOfLightLabel<void> {
using SpeedOfLightLabel<void>::label;
};
} // namespace detail

constexpr auto SPEED_OF_LIGHT = make_constant(detail::SpeedOfLightUnit{});

} // namespace au
36 changes: 36 additions & 0 deletions au/code/au/constants/test/speed_of_light_test.cc
Original file line number Diff line number Diff line change
@@ -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<int>(m / s), SameTypeAndValue(299'792'458 * m / s));
}

TEST(SpeedOfLight, HasExpectedLabel) { EXPECT_THAT(unit_label(SPEED_OF_LIGHT), StrEq("c")); }

} // namespace
} // namespace au
66 changes: 51 additions & 15 deletions tools/bin/make-single-file
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,32 @@ 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)

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
Expand All @@ -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(),
Expand All @@ -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


Expand Down Expand Up @@ -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'<iostream> 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'<iostream> 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:")
Expand Down

0 comments on commit 9b2f9a7

Please sign in to comment.