Skip to content

Commit 67b9e4b

Browse files
authored
Merge pull request #37 from LemLib/feat/std-format
✨ `std::format` support
2 parents a0aa6d8 + dc8f67f commit 67b9e4b

File tree

5 files changed

+100
-27
lines changed

5 files changed

+100
-27
lines changed

.github/workflows/pros-build.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build Template
1+
name: PROS Build Template
22

33
on:
44
push:
@@ -16,14 +16,11 @@ jobs:
1616
uses: actions/checkout@v4
1717

1818
- name: Run LemLib/pros-build
19-
id: build-template
20-
uses: LemLib/[email protected]
21-
with:
22-
copy_readme_and_license_to_include: true
23-
lib_folder_name: units
19+
id: test
20+
uses: LemLib/[email protected]
2421

2522
- name: Upload Artifact
2623
uses: actions/upload-artifact@v4
2724
with:
28-
name: ${{ steps.build-template.outputs.name }}
25+
name: ${{ steps.test.outputs.name }}
2926
path: ${{ github.workspace }}/template/*

include/units/Angle.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ template <> struct LookupName<Quantity<std::ratio<0>, std::ratio<0>, std::ratio<
2121
using Named = Angle;
2222
};
2323

24+
template <> struct std::formatter<Angle> : std::formatter<double> {
25+
auto format(const Angle& number, std::format_context& ctx) const {
26+
auto formatted_double = std::formatter<double>::format(number.internal(), ctx);
27+
return std::format_to(formatted_double, "_stRad");
28+
}
29+
};
30+
2431
inline std::ostream& operator<<(std::ostream& os, const Angle& quantity) {
2532
os << quantity.internal() << " rad";
2633
return os;

include/units/Temperature.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ template <> struct LookupName<Quantity<std::ratio<0>, std::ratio<0>, std::ratio<
2121
using Named = Temperature;
2222
};
2323

24+
template <> struct std::formatter<Temperature> : std::formatter<double> {
25+
auto format(const Temperature& quantity, std::format_context& ctx) const {
26+
return std::format_to(ctx.out(), "{}_k", quantity.internal());
27+
}
28+
};
29+
2430
inline std::ostream& operator<<(std::ostream& os, const Temperature& quantity) {
2531
os << quantity.internal() << " k";
2632
return os;

include/units/units.hpp

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <type_traits>
88
#include <utility>
99
#include <algorithm>
10+
#include <format>
1011

1112
// define M_PI if not already defined
1213
#ifndef M_PI
@@ -183,6 +184,38 @@ template <isQuantity Q, typename quotient> using Rooted = Named<
183184
std::ratio_divide<typename Q::angle, quotient>, std::ratio_divide<typename Q::temperature, quotient>,
184185
std::ratio_divide<typename Q::luminosity, quotient>, std::ratio_divide<typename Q::moles, quotient>>>;
185186

187+
template <isQuantity Q> struct std::formatter<Q> : std::formatter<double> {
188+
auto format(const Q& quantity, std::format_context& ctx) const {
189+
constinit static std::array<std::pair<intmax_t, intmax_t>, 8> dims {{
190+
{Q::mass::num, Q::mass::den},
191+
{Q::length::num, Q::length::den},
192+
{Q::time::num, Q::time::den},
193+
{Q::current::num, Q::current::den},
194+
{Q::angle::num, Q::angle::den},
195+
{Q::temperature::num, Q::temperature::den},
196+
{Q::luminosity::num, Q::luminosity::den},
197+
{Q::moles::num, Q::moles::den},
198+
}};
199+
std::array<const char*, 8> prefixes {"_kg", "_m", "_s", "_A", "_rad", "_K", "_cd", "_mol"};
200+
201+
auto out = ctx.out();
202+
203+
// Format the quantity value
204+
out = std::formatter<double>::format(quantity.internal(), ctx);
205+
206+
// Add dimensions and prefixes
207+
for (size_t i = 0; i != 8; i++) {
208+
if (dims[i].first != 0) {
209+
out = std::format_to(out, "{}", prefixes[i]);
210+
if (dims[i].first != 1 || dims[i].second != 1) { out = std::format_to(out, "^{}", dims[i].first); }
211+
if (dims[i].second != 1) { out = std::format_to(out, "/{}", dims[i].second); }
212+
}
213+
}
214+
215+
return out;
216+
}
217+
};
218+
186219
inline void unit_printer_helper(std::ostream& os, double quantity,
187220
const std::array<std::pair<intmax_t, intmax_t>, 8>& dims) {
188221
static constinit std::array<const char*, 8> prefixes {"_kg", "_m", "_s", "_A", "_rad", "_K", "_cd", "_mol"};
@@ -313,6 +346,12 @@ template <isQuantity Q, isQuantity R> constexpr bool operator>(const Q& lhs, con
313346
return Name(Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, std::ratio<o>, \
314347
std::ratio<j>, std::ratio<n>>(static_cast<double>(value))); \
315348
} \
349+
template <> struct std::formatter<Name> : std::formatter<double> { \
350+
auto format(const Name& number, std::format_context& ctx) const { \
351+
auto formatted_double = std::formatter<double>::format(number.internal(), ctx); \
352+
return std::format_to(formatted_double, "_" #suffix); \
353+
} \
354+
}; \
316355
inline std::ostream& operator<<(std::ostream& os, const Name& quantity) { \
317356
os << quantity.internal() << " " << #suffix; \
318357
return os; \
@@ -355,6 +394,12 @@ constexpr Number operator""_num(unsigned long long value) {
355394
std::ratio<0>, std::ratio<0>>(static_cast<double>(value)));
356395
}
357396

397+
template <> struct std::formatter<Number> : std::formatter<double> {
398+
auto format(const Number& number, std::format_context& ctx) const {
399+
return std::formatter<double>::format(number.internal(), ctx);
400+
}
401+
};
402+
358403
inline std::ostream& operator<<(std::ostream& os, const Number& quantity) {
359404
os << quantity.internal() << " " << num;
360405
return os;
@@ -598,25 +643,30 @@ template <isQuantity Q, isQuantity R> constexpr Q round(const Q& lhs, const R& r
598643

599644
// Convert an angular unit `Q` to a linear unit correctly;
600645
// mostly useful for velocities
601-
template <isQuantity Q> Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current,
602-
typename Q::length, typename Q::temperature, typename Q::luminosity, typename Q::moles>
603-
toLinear(Quantity<typename Q::mass, typename Q::length, typename Q::time, typename Q::current, typename Q::angle,
604-
typename Q::temperature, typename Q::luminosity, typename Q::moles>
605-
angular,
606-
Length diameter) {
646+
template <isQuantity Q>
647+
Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current, typename Q::length,
648+
typename Q::temperature, typename Q::luminosity,
649+
typename Q::moles> constexpr toLinear(Quantity<typename Q::mass, typename Q::length, typename Q::time,
650+
typename Q::current, typename Q::angle, typename Q::temperature,
651+
typename Q::luminosity, typename Q::moles>
652+
angular,
653+
Length diameter) {
607654
return unit_cast<Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current,
608655
typename Q::length, typename Q::temperature, typename Q::luminosity, typename Q::moles>>(
609656
angular * (diameter / 2.0));
610657
}
611658

612659
// Convert an linear unit `Q` to a angular unit correctly;
613660
// mostly useful for velocities
614-
template <isQuantity Q> Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current,
615-
typename Q::length, typename Q::temperature, typename Q::luminosity, typename Q::moles>
616-
toAngular(Quantity<typename Q::mass, typename Q::length, typename Q::time, typename Q::current, typename Q::angle,
617-
typename Q::temperature, typename Q::luminosity, typename Q::moles>
618-
linear,
619-
Length diameter) {
661+
template <isQuantity Q>
662+
Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current, typename Q::length,
663+
typename Q::temperature, typename Q::luminosity,
664+
typename Q::moles> constexpr toAngular(Quantity<typename Q::mass, typename Q::length, typename Q::time,
665+
typename Q::current, typename Q::angle,
666+
typename Q::temperature, typename Q::luminosity,
667+
typename Q::moles>
668+
linear,
669+
Length diameter) {
620670
return unit_cast<Quantity<typename Q::mass, typename Q::angle, typename Q::time, typename Q::current,
621671
typename Q::length, typename Q::temperature, typename Q::luminosity, typename Q::moles>>(
622672
linear / (diameter / 2.0));

src/main.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66

77
constexpr int r2i(double value) { return static_cast<int>(value >= 0.0 ? value + 0.5 : value - 0.5); }
88

9-
/**
10-
* Runs initialization code. This occurs as soon as the program is started.
11-
*
12-
* All other competition modes are blocked by initialize; it is recommended
13-
* to keep execution time for this mode under a few seconds.
14-
*/
159
void initialize() {
10+
std::cout << std::format("{:.2f}", 15.2_cm) << std::endl; // should output 0.15_m
11+
std::cout << std::format("{:.2f}", 180_stDeg) << std::endl; // should output 3.14_stRad
12+
std::cout << std::format("{:.2f}", 0_celsius) << std::endl; // should output 273.15
13+
std::cout << std::format("{:.2f}", 1.2345) << std::endl;
14+
std::cout << units::pow<5>(505_cm) * 15_celsius << std::endl;
15+
std::cout << std::format("{:.2f}", units::pow<5>(505_cm) * 15_celsius) << std::endl;
16+
Number a(2.123);
17+
std::cout << std::format("{:.2f}", a) << std::endl;
18+
}
19+
20+
constexpr void miscTests() {
1621
units::AccelerationPose a(1_mps2, 2_mps2);
1722
Number num = Number(1.0);
1823
num = Number(0.0);
@@ -30,12 +35,18 @@ void initialize() {
3035
std::ratio<0>, std::ratio<0>, std::ratio<0>>(5.0));
3136
units::max(10_celsius, Quantity<std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>,
3237
std::ratio<1>, std::ratio<0>, std::ratio<0>>(1.0));
38+
}
39+
40+
constexpr void v3dTests() {
3341
// check Vector3D overloads
3442
units::Vector3D<Length> v3a = 2 * units::V3Position(2_in, 2_in, 2_in) * 2;
3543
units::Vector3D<Length> v3b = units::V3Position(2_in, 2_in, 2_in) / 2.0;
3644
units::Vector3D<Area> v3c = 2_in * units::V3Position(2_in, 2_in, 2_in);
3745
units::Vector3D<Area> v3d = units::V3Position(2_in, 2_in, 2_in) * 2_in;
3846
units::Vector3D<Number> v3e = units::V3Position(2_in, 2_in, 2_in) / 2_in;
47+
}
48+
49+
constexpr void v2dTests() {
3950
// check Vector2D overloads
4051
units::Vector2D<Length> v2a = units::V2Position(2_in, 2_in) / 2;
4152
units::Vector2D<Length> v2b = 2 * units::V2Position(2_in, 2_in) * 2;
@@ -81,12 +92,14 @@ constexpr double doubleAssignmentTests() {
8192
return d;
8293
}
8394

84-
void numberOperatorTests() {
95+
constexpr void numberOperatorTests() {
8596
using namespace units_double_ops;
8697
static_assert(1_num + 2 == 3);
8798
static_assert(1 + 2_num <= 3);
8899
static_assert(1 / 2_num >= 0);
89100

90101
static_assert(numAssignmentTests() == 0);
91102
static_assert(doubleAssignmentTests() == 1);
92-
}
103+
}
104+
105+
constexpr void formatTests() {}

0 commit comments

Comments
 (0)