Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if(ESP_PLATFORM)
endif()

project(frequency
VERSION 1.2.0
VERSION 1.2.1
DESCRIPTION "Type-safe frequency handling library modeled after std::chrono"
HOMEPAGE_URL "https://github.com/cleishm/frequency-cpp"
LANGUAGES CXX
Expand Down
2 changes: 1 addition & 1 deletion docs/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "frequency"
PROJECT_NUMBER = "1.2.0"
PROJECT_NUMBER = "1.2.1"
PROJECT_BRIEF = "Type-safe frequency handling library modeled after std::chrono"
PROJECT_LOGO =
OUTPUT_DIRECTORY = .
Expand Down
2 changes: 1 addition & 1 deletion idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "1.2.0"
version: "1.2.1"
description: "Type-safe frequency handling library modeled after std::chrono"
url: "https://github.com/cleishm/frequency-cpp"
repository: "https://github.com/cleishm/frequency-cpp.git"
Expand Down
24 changes: 22 additions & 2 deletions include/frequency/frequency.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <compare>
#include <concepts>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <ratio>
#include <string>
Expand Down Expand Up @@ -1156,6 +1157,25 @@ constexpr OutputIt _format_append(OutputIt out, const char* s) {
}
return out;
}

#if CONFIG_FREQUENCY_STD_FORMAT
// Reports an unusable format spec from a formatter's parse().
//
// std::format constant-evaluates parse() to check the format string, so
// throwing there makes a bad spec a compile error rather than a runtime fault.
// Where exceptions are unavailable, calling a non-constexpr function fails that
// same constant evaluation and so reports the error at compile time too; a
// runtime parse (std::vformat with a runtime format string) has no way to
// report it and terminates.
[[noreturn]] inline void _format_error(const char* what) {
#if defined(__cpp_exceptions) && __cpp_exceptions
throw std::format_error(what);
#else
(void)what;
std::abort();
#endif
}
#endif
/** @endcond */

/**
Expand Down Expand Up @@ -1201,8 +1221,8 @@ struct formatter<freq::frequency<Rep, Precision>> {
constexpr auto parse(format_parse_context& ctx) {
auto it = ctx.begin();
if (it == ctx.end() || *it == '}') {
if (_prefix == nullptr) {
throw format_error("frequency: precision has no SI prefix; use an explicit format spec");
if constexpr (_prefix == nullptr) {
freq::_format_error("frequency: precision has no SI prefix; use an explicit format spec");
}
return it;
}
Expand Down
11 changes: 11 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@ target_compile_options(frequency_tests PRIVATE -Wall)
include(CTest)
include(Catch)
catch_discover_tests(frequency_tests)

# The formatter must also build with exceptions disabled, as on embedded
# targets. Catch2 requires exceptions, so this is a standalone target. MSVC
# spells the option differently and still declares __cpp_exceptions, so the
# check is limited to compilers taking -fno-exceptions.
if(NOT MSVC)
add_executable(frequency_no_exceptions_test no_exceptions_test.cpp)
target_link_libraries(frequency_no_exceptions_test PRIVATE frequency::frequency)
target_compile_options(frequency_no_exceptions_test PRIVATE -fno-exceptions)
add_test(NAME no_exceptions COMMAND frequency_no_exceptions_test)
endif()
66 changes: 66 additions & 0 deletions tests/no_exceptions_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Regression test: the std::formatter specialization must be usable when
// compiled with exceptions disabled (-fno-exceptions), as on embedded targets
// such as ESP-IDF with CONFIG_COMPILER_CXX_EXCEPTIONS unset.
//
// This is a separate target from frequency_tests because Catch2 requires
// exceptions. It deliberately avoids any test framework so the whole
// translation unit builds without them.

#include <cstdio>
#include <frequency/frequency>
#include <string>

#ifdef __cpp_exceptions
#error "this test must be compiled with exceptions disabled"
#endif

#if !CONFIG_FREQUENCY_STD_FORMAT

// Without std::format the formatter is not compiled and there is nothing to
// test; older standard libraries (e.g. g++-12's) lack <format> entirely.
int main() {
std::printf("std::format unavailable; nothing to test\n");
return 0;
}

#else

using namespace freq;

namespace {

int failures = 0;

void check(const std::string& actual, const std::string& expected, const char* what) {
if (actual != expected) {
std::printf("FAIL: %s: expected \"%s\", got \"%s\"\n", what, expected.c_str(), actual.c_str());
++failures;
}
}

} // namespace

int main() {
// Formatting with no spec: instantiating parse() must not pull in a throw.
check(std::format("{}", hertz(50)), "50Hz", "{} hertz");
check(std::format("{}", millihertz(1500)), "1500mHz", "{} millihertz");
check(std::format("{}", kilohertz(433)), "433kHz", "{} kilohertz");
check(std::format("{}", megahertz(868)), "868MHz", "{} megahertz");

// Formatting with an explicit spec.
check(std::format("{:.1f}", millihertz(1500)), "1.5Hz", "{:.1f} millihertz");
check(std::format("{:.1f}", kilohertz(433)), "433000.0Hz", "{:.1f} kilohertz");

// A precision with no SI prefix is still usable with an explicit spec.
using decihertz = frequency<int64_t, std::deci>;
check(std::format("{:.1f}", decihertz(225)), "22.5Hz", "{:.1f} decihertz");

if (failures != 0) {
std::printf("%d check(s) failed\n", failures);
return 1;
}
std::printf("all checks passed\n");
return 0;
}

#endif // CONFIG_FREQUENCY_STD_FORMAT
2 changes: 1 addition & 1 deletion vcpkg-port/vcpkg.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cleishm-frequency-cpp",
"version": "1.2.0",
"version": "1.2.1",
"description": "Type-safe frequency handling library modeled after std::chrono",
"homepage": "https://github.com/cleishm/frequency-cpp",
"license": "MIT",
Expand Down
Loading