From fd9901058a6933afcc2ff140eff627ac12c6f278 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Tue, 17 Dec 2024 02:54:35 -0500 Subject: [PATCH 01/46] implement 25% of #297 --- cpp11test/src/test-doubles.cpp | 17 +++++++++++++++++ inst/include/cpp11/doubles.hpp | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/cpp11test/src/test-doubles.cpp b/cpp11test/src/test-doubles.cpp index b17572b8..9115d769 100644 --- a/cpp11test/src/test-doubles.cpp +++ b/cpp11test/src/test-doubles.cpp @@ -5,6 +5,7 @@ #include "cpp11/integers.hpp" #include "cpp11/sexp.hpp" #include "cpp11/strings.hpp" +#include "cpp11/logicals.hpp" #include @@ -454,6 +455,22 @@ context("doubles-C++") { expect_true(!cpp11::is_na(na2[1])); } + test_that("as_doubles(logicals)") { + cpp11::writable::logicals y; + + for (int i = 0; i < 4; i++) { + y.push_back(i % 2 == 0); + } + + cpp11::doubles i(cpp11::as_doubles(y)); + + expect_true(i[0] == 1.0); + expect_true(i[1] == 0.0); + expect_true(i[2] == 1.0); + expect_true(i[3] == 0.0); + expect_true(cpp11::detail::r_typeof(i) == REALSXP); + } + test_that("doubles operator[] and at") { cpp11::doubles x(Rf_allocVector(REALSXP, 2)); REAL(x)[0] = 1; diff --git a/inst/include/cpp11/doubles.hpp b/inst/include/cpp11/doubles.hpp index dcab086d..e48d26af 100644 --- a/inst/include/cpp11/doubles.hpp +++ b/inst/include/cpp11/doubles.hpp @@ -87,6 +87,15 @@ inline doubles as_doubles(SEXP x) { return ret; } + else if (detail::r_typeof(x) == LGLSXP) { + size_t len = Rf_xlength(x); + writable::doubles ret(len); + for (size_t i = 0; i < len; ++i) { + ret[i] = LOGICAL(x)[i] == NA_LOGICAL ? NA_REAL : static_cast(LOGICAL(x)[i]); + } + return ret; + } + throw type_error(REALSXP, detail::r_typeof(x)); } From f5d6054933cec30ce2df446a767e18749f88e2e0 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 27 Dec 2024 03:07:25 -0500 Subject: [PATCH 02/46] clang format --- cpp11test/src/test-doubles.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp11test/src/test-doubles.cpp b/cpp11test/src/test-doubles.cpp index 9115d769..837103ee 100644 --- a/cpp11test/src/test-doubles.cpp +++ b/cpp11test/src/test-doubles.cpp @@ -3,9 +3,9 @@ #include "cpp11/doubles.hpp" #include "cpp11/function.hpp" #include "cpp11/integers.hpp" +#include "cpp11/logicals.hpp" #include "cpp11/sexp.hpp" #include "cpp11/strings.hpp" -#include "cpp11/logicals.hpp" #include @@ -457,7 +457,7 @@ context("doubles-C++") { test_that("as_doubles(logicals)") { cpp11::writable::logicals y; - + for (int i = 0; i < 4; i++) { y.push_back(i % 2 == 0); } From 713f14771f72e526f29c9042671a056eaa2d5810 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 27 Dec 2024 03:34:57 -0500 Subject: [PATCH 03/46] bool -> int + using transform for bool -> double --- cpp11test/src/test-integers.cpp | 17 +++++++++++++++++ inst/include/cpp11/doubles.hpp | 19 +++++++++---------- inst/include/cpp11/integers.hpp | 10 ++++++++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/cpp11test/src/test-integers.cpp b/cpp11test/src/test-integers.cpp index bfc581bf..8caabdb6 100644 --- a/cpp11test/src/test-integers.cpp +++ b/cpp11test/src/test-integers.cpp @@ -2,6 +2,7 @@ #include "cpp11/doubles.hpp" #include "cpp11/function.hpp" #include "cpp11/integers.hpp" +#include "cpp11/logicals.hpp" #include "cpp11/strings.hpp" #include @@ -43,6 +44,22 @@ context("integers-C++") { expect_true(!cpp11::is_na(na2[1])); } + test_that("as_integers(logicals)") { + cpp11::writable::logicals y; + + for (int i = 0; i < 4; i++) { + y.push_back(i % 2 == 0); + } + + cpp11::integers i(cpp11::as_integers(y)); + + expect_true(i[0] == 1); + expect_true(i[1] == 0); + expect_true(i[2] == 1); + expect_true(i[3] == 0); + expect_true(cpp11::detail::r_typeof(i) == INTSXP); + } + test_that("integers.push_back()") { cpp11::writable::integers x; x.push_back(1); diff --git a/inst/include/cpp11/doubles.hpp b/inst/include/cpp11/doubles.hpp index e48d26af..647e3886 100644 --- a/inst/include/cpp11/doubles.hpp +++ b/inst/include/cpp11/doubles.hpp @@ -8,6 +8,7 @@ #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector, REAL #include "cpp11/as.hpp" // for as_sexp #include "cpp11/protect.hpp" // for safe +#include "cpp11/r_bool.hpp" // for r_bool #include "cpp11/r_vector.hpp" // for vector, vector<>::proxy, vector<>::... #include "cpp11/sexp.hpp" // for sexp @@ -71,13 +72,12 @@ typedef r_vector doubles; } // namespace writable typedef r_vector integers; +typedef r_vector logicals; inline doubles as_doubles(SEXP x) { if (detail::r_typeof(x) == REALSXP) { return doubles(x); - } - - else if (detail::r_typeof(x) == INTSXP) { + } else if (detail::r_typeof(x) == INTSXP) { integers xn(x); size_t len = xn.size(); writable::doubles ret(len); @@ -85,14 +85,13 @@ inline doubles as_doubles(SEXP x) { return value == NA_INTEGER ? NA_REAL : static_cast(value); }); return ret; - } - - else if (detail::r_typeof(x) == LGLSXP) { - size_t len = Rf_xlength(x); + } else if (detail::r_typeof(x) == LGLSXP) { + logicals xn(x); + size_t len = xn.size(); writable::doubles ret(len); - for (size_t i = 0; i < len; ++i) { - ret[i] = LOGICAL(x)[i] == NA_LOGICAL ? NA_REAL : static_cast(LOGICAL(x)[i]); - } + std::transform(xn.begin(), xn.end(), ret.begin(), [](bool value) { + return value == NA_LOGICAL ? NA_REAL : static_cast(value); + }); return ret; } diff --git a/inst/include/cpp11/integers.hpp b/inst/include/cpp11/integers.hpp index 68fd8e4d..b10d6e70 100644 --- a/inst/include/cpp11/integers.hpp +++ b/inst/include/cpp11/integers.hpp @@ -9,6 +9,7 @@ #include "cpp11/as.hpp" // for as_sexp #include "cpp11/attribute_proxy.hpp" // for attribute_proxy #include "cpp11/protect.hpp" // for safe +#include "cpp11/r_bool.hpp" // for r_bool #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy #include "cpp11/sexp.hpp" // for sexp @@ -79,6 +80,7 @@ inline int na() { // forward declaration typedef r_vector doubles; +typedef r_vector logicals; inline integers as_integers(SEXP x) { if (detail::r_typeof(x) == INTSXP) { @@ -96,6 +98,14 @@ inline integers as_integers(SEXP x) { return static_cast(value); }); return ret; + } else if (detail::r_typeof(x) == LGLSXP) { + logicals xn(x); + size_t len = xn.size(); + writable::integers ret(len); + std::transform(xn.begin(), xn.end(), ret.begin(), [](bool value) { + return value == NA_LOGICAL ? NA_INTEGER : static_cast(value); + }); + return ret; } throw type_error(INTSXP, detail::r_typeof(x)); From bbee709e6d6660e98401f2330eaf233d614c9ec4 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Wed, 1 Jan 2025 04:38:32 -0500 Subject: [PATCH 04/46] fix #406 --- cpp11test/DESCRIPTION | 2 +- inst/include/cpp11/r_vector.hpp | 4 ++- inst/include/cpp11/strings.hpp | 51 +++++++++++++++++++++++---------- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/cpp11test/DESCRIPTION b/cpp11test/DESCRIPTION index d1d05665..70c5649f 100644 --- a/cpp11test/DESCRIPTION +++ b/cpp11test/DESCRIPTION @@ -20,4 +20,4 @@ Suggests: xml2 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.2 diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 576f4fe6..c27f54e9 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -235,7 +235,9 @@ class r_vector : public cpp11::r_vector { proxy at(const r_string& name) const; void push_back(T value); - /// Implemented in `strings.hpp` + template ::value>::type* = nullptr> + void push_back(const std::string& value); // Pacha: r_string only (#406) void push_back(const named_arg& value); void pop_back(); diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 393f2a8c..cd279192 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -61,12 +61,32 @@ typedef r_vector strings; namespace writable { template <> -inline void r_vector::set_elt(SEXP x, R_xlen_t i, - typename r_vector::underlying_type value) { +inline void r_vector::set_elt( + SEXP x, R_xlen_t i, typename r_vector::underlying_type value) { // NOPROTECT: Likely too costly to unwind protect every set elt SET_STRING_ELT(x, i, value); } +// Pacha: Optimized subscript assignment for std::string +template <> +inline typename r_vector::proxy r_vector::operator[]( + R_xlen_t i) const { + return typename r_vector::proxy(data_, i, nullptr, false); +} + +// Pacha: Optimized push_back for std::string +template <> +template ::value>::type*> +inline void r_vector::push_back(const std::string& value) { + while (this->length_ >= this->capacity_) { + this->reserve(this->capacity_ == 0 ? 1 : this->capacity_ * 2); + } + + SEXP char_sexp = Rf_mkCharLenCE(value.c_str(), value.size(), CE_UTF8); + SET_STRING_ELT(this->data_, this->length_, char_sexp); + ++this->length_; +} + inline bool operator==(const r_vector::proxy& lhs, r_string rhs) { return static_cast(lhs).operator==(static_cast(rhs).c_str()); } @@ -95,17 +115,17 @@ inline SEXP alloc_if_charsxp(const SEXP data) { template <> inline r_vector::r_vector(const SEXP& data) - : cpp11::r_vector(alloc_or_copy(data)), capacity_(length_) { + : cpp11::r_vector(alloc_or_copy(data)), capacity_(this->length_) { if (detail::r_typeof(data) == CHARSXP) { - SET_STRING_ELT(data_, 0, data); + SET_STRING_ELT(this->data_, 0, data); } } template <> inline r_vector::r_vector(SEXP&& data) - : cpp11::r_vector(alloc_if_charsxp(data)), capacity_(length_) { + : cpp11::r_vector(alloc_if_charsxp(data)), capacity_(this->length_) { if (detail::r_typeof(data) == CHARSXP) { - SET_STRING_ELT(data_, 0, data); + SET_STRING_ELT(this->data_, 0, data); } } @@ -117,14 +137,15 @@ inline r_vector::r_vector(std::initializer_list il) unwind_protect([&] { auto it = il.begin(); - for (R_xlen_t i = 0; i < capacity_; ++i, ++it) { + for (R_xlen_t i = 0; i < this->capacity_; ++i, ++it) { // i.e. to `SEXP` - underlying_type elt = static_cast(*it); + typename r_vector::underlying_type elt = + static_cast::underlying_type>(*it); if (elt == NA_STRING) { - set_elt(data_, i, elt); + set_elt(this->data_, i, elt); } else { - set_elt(data_, i, Rf_mkCharCE(Rf_translateCharUTF8(elt), CE_UTF8)); + set_elt(this->data_, i, Rf_mkCharCE(Rf_translateCharUTF8(elt), CE_UTF8)); } } }); @@ -135,12 +156,12 @@ typedef r_vector strings; template inline void r_vector::push_back(const named_arg& value) { push_back(value.value()); - if (Rf_xlength(names()) == 0) { - cpp11::writable::strings new_nms(size()); - names() = new_nms; + if (Rf_xlength(this->names()) == 0) { + cpp11::writable::strings new_nms(this->size()); + this->names() = new_nms; } - cpp11::writable::strings nms(names()); - nms[size() - 1] = value.name(); + cpp11::writable::strings nms(this->names()); + nms[this->size() - 1] = value.name(); } } // namespace writable From 496dd6ba2ecfd212d8564d8cd46657971b79b0f3 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Wed, 1 Jan 2025 05:37:35 -0500 Subject: [PATCH 05/46] borrow from @traversc's push_back_fast --- inst/include/cpp11/strings.hpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index cd279192..fdf84405 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -67,23 +67,15 @@ inline void r_vector::set_elt( SET_STRING_ELT(x, i, value); } -// Pacha: Optimized subscript assignment for std::string -template <> -inline typename r_vector::proxy r_vector::operator[]( - R_xlen_t i) const { - return typename r_vector::proxy(data_, i, nullptr, false); -} - -// Pacha: Optimized push_back for std::string +// Pacha: Optimized push_back for std::string (borrows from @traversc' push_back_fast) template <> template ::value>::type*> inline void r_vector::push_back(const std::string& value) { while (this->length_ >= this->capacity_) { this->reserve(this->capacity_ == 0 ? 1 : this->capacity_ * 2); } - - SEXP char_sexp = Rf_mkCharLenCE(value.c_str(), value.size(), CE_UTF8); - SET_STRING_ELT(this->data_, this->length_, char_sexp); + set_elt(this->data_, this->length_, + Rf_mkCharLenCE(value.c_str(), value.size(), CE_UTF8)); ++this->length_; } From 008ab7c199b51a03f3ea8d6be4aadb22cea76b48 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Wed, 1 Jan 2025 05:37:48 -0500 Subject: [PATCH 06/46] add benchmarks --- cpp11test/R/cpp11.R | 20 +++++++++ cpp11test/bench/strings.R | 35 +++++++++++++++ cpp11test/src/cpp11.cpp | 40 +++++++++++++++++ cpp11test/src/strings.cpp | 94 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+) create mode 100644 cpp11test/bench/strings.R diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 038e7b76..da4588eb 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -168,6 +168,26 @@ string_push_back_ <- function() { .Call(`_cpp11test_string_push_back_`) } +grow_strings_cpp11_ <- function(n, seed) { + .Call(`_cpp11test_grow_strings_cpp11_`, n, seed) +} + +grow_strings_rcpp_ <- function(n, seed) { + .Call(`_cpp11test_grow_strings_rcpp_`, n, seed) +} + +grow_strings_manual_ <- function(n, seed) { + .Call(`_cpp11test_grow_strings_manual_`, n, seed) +} + +assign_cpp11_ <- function(n, seed) { + .Call(`_cpp11test_assign_cpp11_`, n, seed) +} + +assign_rcpp_ <- function(n, seed) { + .Call(`_cpp11test_assign_rcpp_`, n, seed) +} + sum_dbl_for_ <- function(x) { .Call(`_cpp11test_sum_dbl_for_`, x) } diff --git a/cpp11test/bench/strings.R b/cpp11test/bench/strings.R new file mode 100644 index 00000000..e497f68d --- /dev/null +++ b/cpp11test/bench/strings.R @@ -0,0 +1,35 @@ +pkgload::load_all("cpp11test") + +bench::press(len = as.integer(10^(0:6)), { + bench::mark( + assign_cpp11_(n = len, 123L), + assign_rcpp_(n = len, 123L), + iterations = 20 + ) +})[c("expression", "len", "min", "mem_alloc", "n_itr", "n_gc")] + +# Longer benchmark, lots of gc +len <- as.integer(10^7) +bench::mark( + cpp11 = cpp11_push_and_truncate_(len), + rcpp = rcpp_push_and_truncate_(len), + min_iterations = 200 +)[c("expression", "min", "mem_alloc", "n_itr", "n_gc")] + +bench::press(len = as.integer(10^(0:6)), { + bench::mark( + grow_strings_cpp11_(len, 123L), + grow_strings_rcpp_(len, 123L), + grow_strings_manual_(len, 123L), + iterations = 20 + ) +})[c("expression", "len", "min", "mem_alloc", "n_itr", "n_gc")] + +# Longer benchmark, lots of gc +len <- as.integer(10^7) +bench::mark( + cpp11 = cpp11_grow_strings_(len), + rcpp = rcpp_grow_strings_(len), + manual = manual_grow_strings_(len), + min_iterations = 200 +)[c("expression", "min", "mem_alloc", "n_itr", "n_gc")] diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 421de637..df3a428e 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -324,6 +324,41 @@ extern "C" SEXP _cpp11test_string_push_back_() { return cpp11::as_sexp(string_push_back_()); END_CPP11 } +// strings.cpp +cpp11::strings grow_strings_cpp11_(size_t n, int seed); +extern "C" SEXP _cpp11test_grow_strings_cpp11_(SEXP n, SEXP seed) { + BEGIN_CPP11 + return cpp11::as_sexp(grow_strings_cpp11_(cpp11::as_cpp>(n), cpp11::as_cpp>(seed))); + END_CPP11 +} +// strings.cpp +Rcpp::CharacterVector grow_strings_rcpp_(size_t n, int seed); +extern "C" SEXP _cpp11test_grow_strings_rcpp_(SEXP n, SEXP seed) { + BEGIN_CPP11 + return cpp11::as_sexp(grow_strings_rcpp_(cpp11::as_cpp>(n), cpp11::as_cpp>(seed))); + END_CPP11 +} +// strings.cpp +SEXP grow_strings_manual_(size_t n, int seed); +extern "C" SEXP _cpp11test_grow_strings_manual_(SEXP n, SEXP seed) { + BEGIN_CPP11 + return cpp11::as_sexp(grow_strings_manual_(cpp11::as_cpp>(n), cpp11::as_cpp>(seed))); + END_CPP11 +} +// strings.cpp +cpp11::strings assign_cpp11_(size_t n, int seed); +extern "C" SEXP _cpp11test_assign_cpp11_(SEXP n, SEXP seed) { + BEGIN_CPP11 + return cpp11::as_sexp(assign_cpp11_(cpp11::as_cpp>(n), cpp11::as_cpp>(seed))); + END_CPP11 +} +// strings.cpp +Rcpp::CharacterVector assign_rcpp_(size_t n, int seed); +extern "C" SEXP _cpp11test_assign_rcpp_(SEXP n, SEXP seed) { + BEGIN_CPP11 + return cpp11::as_sexp(assign_rcpp_(cpp11::as_cpp>(n), cpp11::as_cpp>(seed))); + END_CPP11 +} // sum.cpp double sum_dbl_for_(cpp11::doubles x); extern "C" SEXP _cpp11test_sum_dbl_for_(SEXP x) { @@ -472,6 +507,8 @@ extern "C" { extern SEXP run_testthat_tests(SEXP); static const R_CallMethodDef CallEntries[] = { + {"_cpp11test_assign_cpp11_", (DL_FUNC) &_cpp11test_assign_cpp11_, 2}, + {"_cpp11test_assign_rcpp_", (DL_FUNC) &_cpp11test_assign_rcpp_, 2}, {"_cpp11test_col_sums", (DL_FUNC) &_cpp11test_col_sums, 1}, {"_cpp11test_cpp11_add_vec_for_", (DL_FUNC) &_cpp11test_cpp11_add_vec_for_, 2}, {"_cpp11test_cpp11_insert_", (DL_FUNC) &_cpp11test_cpp11_insert_, 1}, @@ -488,6 +525,9 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_gibbs_rcpp", (DL_FUNC) &_cpp11test_gibbs_rcpp, 2}, {"_cpp11test_gibbs_rcpp2", (DL_FUNC) &_cpp11test_gibbs_rcpp2, 2}, {"_cpp11test_grow_", (DL_FUNC) &_cpp11test_grow_, 1}, + {"_cpp11test_grow_strings_cpp11_", (DL_FUNC) &_cpp11test_grow_strings_cpp11_, 2}, + {"_cpp11test_grow_strings_manual_", (DL_FUNC) &_cpp11test_grow_strings_manual_, 2}, + {"_cpp11test_grow_strings_rcpp_", (DL_FUNC) &_cpp11test_grow_strings_rcpp_, 2}, {"_cpp11test_my_message", (DL_FUNC) &_cpp11test_my_message, 2}, {"_cpp11test_my_message_n1", (DL_FUNC) &_cpp11test_my_message_n1, 1}, {"_cpp11test_my_message_n1fmt", (DL_FUNC) &_cpp11test_my_message_n1fmt, 1}, diff --git a/cpp11test/src/strings.cpp b/cpp11test/src/strings.cpp index 1db7736b..e19188dc 100644 --- a/cpp11test/src/strings.cpp +++ b/cpp11test/src/strings.cpp @@ -1,4 +1,8 @@ #include "cpp11/strings.hpp" +#include +#include + +#include // Test benchmark for string proxy assignment performance. // We don't unwind_protect() before each `SET_STRING_ELT()` call, @@ -33,3 +37,93 @@ return x; } + +// issue 406 + +std::random_device rd; +std::mt19937 gen(rd()); + +double random_double() { + std::uniform_real_distribution dist(0.0, 1.0); + return dist(gen); +} + +int random_int(int min, int max) { + std::uniform_int_distribution dist(min, max); + return dist(gen); +} + +std::string random_string() { + std::string s(10, '\0'); + for (size_t i = 0; i < 10; i++) { + s[i] = random_int(0, 25) + 'a'; + } + return s; +} + +[[cpp11::register]] cpp11::strings grow_strings_cpp11_(size_t n, int seed) { + gen.seed(seed); + cpp11::writable::strings x; + for (size_t i = 0; i < n; ++i) { + x.push_back(random_string()); + } + return x; +} + +[[cpp11::register]] Rcpp::CharacterVector grow_strings_rcpp_(size_t n, int seed) { + gen.seed(seed); + Rcpp::CharacterVector x(n); + for (size_t i = 0; i < n; ++i) { + x[i] = random_string(); + } + return x; +} + +[[cpp11::register]] SEXP grow_strings_manual_(size_t n, int seed) { + gen.seed(seed); + SEXP data_ = PROTECT(Rf_allocVector(STRSXP, 0)); + size_t size_ = 0; + size_t capacity_ = 0; + for (size_t i = 0; i < n; ++i) { + if (size_ == capacity_) { + capacity_ = capacity_ == 0 ? 1 : capacity_ * 2; + SEXP new_data_ = PROTECT(Rf_allocVector(STRSXP, capacity_)); + for (size_t j = 0; j < size_; ++j) { + SET_STRING_ELT(new_data_, j, STRING_ELT(data_, j)); + } + UNPROTECT(2); + data_ = PROTECT(new_data_); + } + SET_STRING_ELT(data_, size_++, Rf_mkChar(random_string().c_str())); + } + // copy back down to size + if (size_ < capacity_) { + SEXP new_data_ = PROTECT(Rf_allocVector(STRSXP, size_)); + for (size_t j = 0; j < size_; ++j) { + SET_STRING_ELT(new_data_, j, STRING_ELT(data_, j)); + } + UNPROTECT(2); + return new_data_; + } else { + UNPROTECT(1); + return data_; + } +} + +[[cpp11::register]] cpp11::strings assign_cpp11_(size_t n, int seed) { + gen.seed(seed); + cpp11::writable::strings x(n); + for (size_t i = 0; i < n; ++i) { + x[i] = random_string(); + } + return x; +} + +[[cpp11::register]] Rcpp::CharacterVector assign_rcpp_(size_t n, int seed) { + gen.seed(seed); + Rcpp::CharacterVector x(n); + for (size_t i = 0; i < n; ++i) { + x[i] = random_string(); + } + return x; +} From 9673bb27d9e3b4e75d6f57068b316c4bbe329a8b Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Wed, 1 Jan 2025 07:11:04 -0500 Subject: [PATCH 07/46] implement #435 --- cpp11test/DESCRIPTION | 2 +- inst/include/cpp11/r_vector.hpp | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/cpp11test/DESCRIPTION b/cpp11test/DESCRIPTION index d1d05665..70c5649f 100644 --- a/cpp11test/DESCRIPTION +++ b/cpp11test/DESCRIPTION @@ -20,4 +20,4 @@ Suggests: xml2 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.2 diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 576f4fe6..9eb240be 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -876,15 +876,23 @@ inline r_vector::r_vector(std::initializer_list il) // SAFETY: We've validated type and length ahead of this. const underlying_type elt = get_elt(value, 0); - // TODO: The equivalent ctor from `initializer_list` has a specialization - // for `` to translate `elt` to UTF-8 before assigning. Should we have - // that here too? `named_arg` doesn't do any checking here. - if (data_p_ != nullptr) { - data_p_[i] = elt; + if constexpr (std::is_same::value) { + // Translate to UTF-8 before assigning for string types + SEXP translated_elt = Rf_mkCharCE(Rf_translateCharUTF8(elt), CE_UTF8); + + if (data_p_ != nullptr) { + data_p_[i] = translated_elt; + } else { + // Handles STRSXP case. VECSXP case has its own specialization. + // We don't expect any ALTREP cases since we just freshly allocated `data_`. + set_elt(data_, i, translated_elt); + } } else { - // Handles STRSXP case. VECSXP case has its own specialization. - // We don't expect any ALTREP cases since we just freshly allocated `data_`. - set_elt(data_, i, elt); + if (data_p_ != nullptr) { + data_p_[i] = elt; + } else { + set_elt(data_, i, elt); + } } SEXP name = Rf_mkCharCE(it->name(), CE_UTF8); From a03a1267e708d1eb50aadfee6454a7ccd31c8c65 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Wed, 1 Jan 2025 16:58:09 -0500 Subject: [PATCH 08/46] draft map -> list/sexp --- cpp11test/DESCRIPTION | 2 +- inst/include/cpp11/map_to_sexp.hpp | 44 ++++++++++++++++++++++++++++ vignettes/motivations.Rmd | 47 +++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 inst/include/cpp11/map_to_sexp.hpp diff --git a/cpp11test/DESCRIPTION b/cpp11test/DESCRIPTION index d1d05665..70c5649f 100644 --- a/cpp11test/DESCRIPTION +++ b/cpp11test/DESCRIPTION @@ -20,4 +20,4 @@ Suggests: xml2 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.2 diff --git a/inst/include/cpp11/map_to_sexp.hpp b/inst/include/cpp11/map_to_sexp.hpp new file mode 100644 index 00000000..62ef2e11 --- /dev/null +++ b/inst/include/cpp11/map_to_sexp.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "cpp11/R.hpp" +#include "cpp11/protect.hpp" +#include "cpp11/list.hpp" +#include "cpp11/strings.hpp" + +namespace cpp11 { + +template +SEXP map_to_sexp(const std::map& map) { + cpp11::writable::list result(map.size()); + cpp11::writable::strings names(map.size()); + + size_t i = 0; + for (const auto& pair : map) { + result[i] = cpp11::as_sexp(pair.second); + names[i] = cpp11::as_sexp(pair.first); + ++i; + } + + result.names() = names; + return result; +} + +template +SEXP unordered_map_to_sexp(const std::unordered_map& map) { + cpp11::writable::list result(map.size()); + cpp11::writable::strings names(map.size()); + + size_t i = 0; + for (const auto& pair : map) { + result[i] = cpp11::as_sexp(pair.second); + names[i] = cpp11::as_sexp(pair.first); + ++i; + } + + result.names() = names; + return result; +} + +} // namespace cpp11 diff --git a/vignettes/motivations.Rmd b/vignettes/motivations.Rmd index 9f5ffb0f..4e957dcd 100644 --- a/vignettes/motivations.Rmd +++ b/vignettes/motivations.Rmd @@ -332,7 +332,52 @@ Doing this universally avoids many locale specific issues when dealing with Unic Concretely cpp11 always uses `Rf_translateCharUTF8()` when obtaining `const char*` from `CHRSXP` objects and uses `Rf_mkCharCE(, CE_UTF8)` when creating new `CHRSXP` objects from `const char*` inputs. - +Converting R Unicode Strings to C++ Strings: + +```cpp +#include +#include + +[[cpp11::register]] +std::string convert_to_utf8(cpp11::strings input) { + std::string result; + for (auto str : input) { + result += cpp11::r_string(Rf_translateCharUTF8(str)); + } + return result; +} +``` + +```r +# hello + how are you? in Japanese and Spanish +input <- c("\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf", + "\xc2\xbfC\xc3\xb3mo est\xc3\xa1s\x3f") + +convert_to_utf8(input) + +[1] "こんにちは¿Cómo estás?" +``` + +Returning Unicode Strings from C++ to R: + +```cpp +#include +#include + +[[cpp11::register]] +cpp11::writable::strings return_utf8_string() { + cpp11::writable::strings result; + std::string utf8_str = "こんにちは"; // Hello in Japanese + result.push_back(Rf_mkCharCE(utf8_str.c_str(), CE_UTF8)); + return result; +} +``` + +```r +return_utf8_string() + +[1] "こんにちは" +``` ## C++11 features {#c11-features} From aaed10470838c6836168e3d7ac904cb6fa7a21a6 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Thu, 2 Jan 2025 01:19:25 -0500 Subject: [PATCH 09/46] ordered and unordered C++ maps are converted to R lists --- cpp11test/R/cpp11.R | 8 ++ cpp11test/src/cpp11.cpp | 16 ++++ cpp11test/src/map.cpp | 20 +++++ cpp11test/tests/testthat/test-map-to-list.R | 18 +++++ inst/include/cpp11/as.hpp | 84 +++++++++++++++++++-- inst/include/cpp11/map_to_sexp.hpp | 44 ----------- vignettes/cpp11.Rmd | 4 - 7 files changed, 140 insertions(+), 54 deletions(-) create mode 100644 cpp11test/src/map.cpp create mode 100644 cpp11test/tests/testthat/test-map-to-list.R delete mode 100644 inst/include/cpp11/map_to_sexp.hpp diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 038e7b76..332faa88 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -88,6 +88,14 @@ cpp11_insert_ <- function(num_sxp) { .Call(`_cpp11test_cpp11_insert_`, num_sxp) } +ordered_map_to_list_ <- function(x) { + .Call(`_cpp11test_ordered_map_to_list_`, x) +} + +unordered_map_to_list_ <- function(x) { + .Call(`_cpp11test_unordered_map_to_list_`, x) +} + gibbs_cpp <- function(N, thin) { .Call(`_cpp11test_gibbs_cpp`, N, thin) } diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 421de637..56057fc0 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -173,6 +173,20 @@ extern "C" SEXP _cpp11test_cpp11_insert_(SEXP num_sxp) { return cpp11::as_sexp(cpp11_insert_(cpp11::as_cpp>(num_sxp))); END_CPP11 } +// map.cpp +SEXP ordered_map_to_list_(cpp11::doubles x); +extern "C" SEXP _cpp11test_ordered_map_to_list_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(ordered_map_to_list_(cpp11::as_cpp>(x))); + END_CPP11 +} +// map.cpp +SEXP unordered_map_to_list_(cpp11::doubles x); +extern "C" SEXP _cpp11test_unordered_map_to_list_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(unordered_map_to_list_(cpp11::as_cpp>(x))); + END_CPP11 +} // matrix.cpp SEXP gibbs_cpp(int N, int thin); extern "C" SEXP _cpp11test_gibbs_cpp(SEXP N, SEXP thin) { @@ -500,6 +514,7 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_my_warning_n1", (DL_FUNC) &_cpp11test_my_warning_n1, 1}, {"_cpp11test_my_warning_n1fmt", (DL_FUNC) &_cpp11test_my_warning_n1fmt, 1}, {"_cpp11test_my_warning_n2fmt", (DL_FUNC) &_cpp11test_my_warning_n2fmt, 2}, + {"_cpp11test_ordered_map_to_list_", (DL_FUNC) &_cpp11test_ordered_map_to_list_, 1}, {"_cpp11test_protect_many_", (DL_FUNC) &_cpp11test_protect_many_, 1}, {"_cpp11test_protect_many_cpp11_", (DL_FUNC) &_cpp11test_protect_many_cpp11_, 1}, {"_cpp11test_protect_many_preserve_", (DL_FUNC) &_cpp11test_protect_many_preserve_, 1}, @@ -533,6 +548,7 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_sum_int_foreach_", (DL_FUNC) &_cpp11test_sum_int_foreach_, 1}, {"_cpp11test_test_destruction_inner", (DL_FUNC) &_cpp11test_test_destruction_inner, 0}, {"_cpp11test_test_destruction_outer", (DL_FUNC) &_cpp11test_test_destruction_outer, 0}, + {"_cpp11test_unordered_map_to_list_", (DL_FUNC) &_cpp11test_unordered_map_to_list_, 1}, {"_cpp11test_upper_bound", (DL_FUNC) &_cpp11test_upper_bound, 2}, {"run_testthat_tests", (DL_FUNC) &run_testthat_tests, 1}, {NULL, NULL, 0} diff --git a/cpp11test/src/map.cpp b/cpp11test/src/map.cpp new file mode 100644 index 00000000..e4a32db4 --- /dev/null +++ b/cpp11test/src/map.cpp @@ -0,0 +1,20 @@ +#include "cpp11/as.hpp" +#include "cpp11/doubles.hpp" + +[[cpp11::register]] SEXP ordered_map_to_list_(cpp11::doubles x) { + std::map counts; + int n = x.size(); + for (int i = 0; i < n; i++) { + counts[x[i]]++; + } + return cpp11::as_sexp(counts); +} + +[[cpp11::register]] SEXP unordered_map_to_list_(cpp11::doubles x) { + std::unordered_map counts; + int n = x.size(); + for (int i = 0; i < n; i++) { + counts[x[i]]++; + } + return cpp11::as_sexp(counts); +} diff --git a/cpp11test/tests/testthat/test-map-to-list.R b/cpp11test/tests/testthat/test-map-to-list.R new file mode 100644 index 00000000..dc637afa --- /dev/null +++ b/cpp11test/tests/testthat/test-map-to-list.R @@ -0,0 +1,18 @@ +test_that("ordered and unordered C++ maps are converted to R lists", { + set.seed(42L) + x <- rnorm(10L) + xprime <- c(x, x[1]) + + om <- ordered_map_to_list_(x) + expect_type(om, "list") + + om_doubles <- as.double(names(om)) + expect_equal(om_doubles, sort(om_doubles)) + + omprime <- ordered_map_to_list_(xprime) + expect_equal(unlist(unique(omprime)), 1:2) + + um <- unordered_map_to_list_(xprime) + expect_type(um, "list") + expect_equal(unlist(unique(um)), 1:2) +}) diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 682f12b5..374c0518 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -2,10 +2,13 @@ #include // for modf #include // for initializer_list +#include // for std::map #include // for std::shared_ptr, std::weak_ptr, std::unique_ptr #include -#include // for string, basic_string -#include // for decay, enable_if, is_same, is_convertible +#include // for string, basic_string +#include // for decay, enable_if, is_same, is_convertible +#include // for std::unordered_map +#include // for std::vector #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_xlength, R_xlen_t #include "cpp11/protect.hpp" // for stop, protect, safe, protect::function @@ -243,7 +246,7 @@ enable_if_integral as_sexp(const Container& from) { } inline SEXP as_sexp(std::initializer_list from) { - return as_sexp>(from); + return as_sexp(std::vector(from)); } template as_sexp(const Container& from) { } inline SEXP as_sexp(std::initializer_list from) { - return as_sexp>(from); + return as_sexp(std::vector(from)); } template as_sexp(const Container& from) { } inline SEXP as_sexp(std::initializer_list from) { - return as_sexp>(from); + return as_sexp(std::vector(from)); } namespace detail { @@ -325,7 +328,7 @@ enable_if_c_string as_sexp(const Container& from) { } inline SEXP as_sexp(std::initializer_list from) { - return as_sexp>(from); + return as_sexp(std::vector(from)); } template > @@ -333,4 +336,73 @@ enable_if_convertible_to_sexp as_sexp(const T& from) { return from; } +// Pacha: Specialization for std::map +// NOTE: I did not use templates to avoid clashes with doubles/function/etc. +inline SEXP as_sexp(const std::map& map) { + R_xlen_t size = map.size(); + SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); + SEXP names = PROTECT(Rf_allocVector(STRSXP, size)); + + auto it = map.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + SET_VECTOR_ELT(result, i, it->second); + SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8)); + } + + Rf_setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + +// Specialization for std::map +inline SEXP as_sexp(const std::map& map) { + R_xlen_t size = map.size(); + SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); + SEXP names = PROTECT(Rf_allocVector(REALSXP, size)); + + auto it = map.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second)); + REAL(names)[i] = it->first; + } + + Rf_setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + +// Pacha: Specialization for std::unordered_map +inline SEXP as_sexp(const std::unordered_map& map) { + R_xlen_t size = map.size(); + SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); + SEXP names = PROTECT(Rf_allocVector(STRSXP, size)); + + auto it = map.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + SET_VECTOR_ELT(result, i, it->second); + SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8)); + } + + Rf_setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + +// Specialization for std::unordered_map +inline SEXP as_sexp(const std::unordered_map& map) { + R_xlen_t size = map.size(); + SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); + SEXP names = PROTECT(Rf_allocVector(REALSXP, size)); + + auto it = map.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second)); + REAL(names)[i] = it->first; + } + + Rf_setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + } // namespace cpp11 diff --git a/inst/include/cpp11/map_to_sexp.hpp b/inst/include/cpp11/map_to_sexp.hpp deleted file mode 100644 index 62ef2e11..00000000 --- a/inst/include/cpp11/map_to_sexp.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include "cpp11/R.hpp" -#include "cpp11/protect.hpp" -#include "cpp11/list.hpp" -#include "cpp11/strings.hpp" - -namespace cpp11 { - -template -SEXP map_to_sexp(const std::map& map) { - cpp11::writable::list result(map.size()); - cpp11::writable::strings names(map.size()); - - size_t i = 0; - for (const auto& pair : map) { - result[i] = cpp11::as_sexp(pair.second); - names[i] = cpp11::as_sexp(pair.first); - ++i; - } - - result.names() = names; - return result; -} - -template -SEXP unordered_map_to_sexp(const std::unordered_map& map) { - cpp11::writable::list result(map.size()); - cpp11::writable::strings names(map.size()); - - size_t i = 0; - for (const auto& pair : map) { - result[i] = cpp11::as_sexp(pair.second); - names[i] = cpp11::as_sexp(pair.first); - ++i; - } - - result.names() = names; - return result; -} - -} // namespace cpp11 diff --git a/vignettes/cpp11.Rmd b/vignettes/cpp11.Rmd index 5f10fcc6..dcae8e05 100644 --- a/vignettes/cpp11.Rmd +++ b/vignettes/cpp11.Rmd @@ -878,8 +878,6 @@ logicals duplicated_cpp(integers x) { } ``` -````{=html} - -```` ### Exercises From 70c68dfd22382dfb377e75beb2e79c43db1f2cdd Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Thu, 2 Jan 2025 01:26:45 -0500 Subject: [PATCH 10/46] template map to list conversion --- cpp11test/R/cpp11.R | 4 ++ cpp11test/src/cpp11.cpp | 8 ++++ cpp11test/src/map.cpp | 9 ++++ cpp11test/tests/testthat/test-map-to-list.R | 3 ++ inst/include/cpp11/as.hpp | 53 +++++---------------- 5 files changed, 36 insertions(+), 41 deletions(-) diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 332faa88..4c323020 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -92,6 +92,10 @@ ordered_map_to_list_ <- function(x) { .Call(`_cpp11test_ordered_map_to_list_`, x) } +ordered_map_to_list_2_ <- function(x) { + .Call(`_cpp11test_ordered_map_to_list_2_`, x) +} + unordered_map_to_list_ <- function(x) { .Call(`_cpp11test_unordered_map_to_list_`, x) } diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 56057fc0..4ce4e154 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -181,6 +181,13 @@ extern "C" SEXP _cpp11test_ordered_map_to_list_(SEXP x) { END_CPP11 } // map.cpp +SEXP ordered_map_to_list_2_(cpp11::doubles x); +extern "C" SEXP _cpp11test_ordered_map_to_list_2_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(ordered_map_to_list_2_(cpp11::as_cpp>(x))); + END_CPP11 +} +// map.cpp SEXP unordered_map_to_list_(cpp11::doubles x); extern "C" SEXP _cpp11test_unordered_map_to_list_(SEXP x) { BEGIN_CPP11 @@ -515,6 +522,7 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_my_warning_n1fmt", (DL_FUNC) &_cpp11test_my_warning_n1fmt, 1}, {"_cpp11test_my_warning_n2fmt", (DL_FUNC) &_cpp11test_my_warning_n2fmt, 2}, {"_cpp11test_ordered_map_to_list_", (DL_FUNC) &_cpp11test_ordered_map_to_list_, 1}, + {"_cpp11test_ordered_map_to_list_2_", (DL_FUNC) &_cpp11test_ordered_map_to_list_2_, 1}, {"_cpp11test_protect_many_", (DL_FUNC) &_cpp11test_protect_many_, 1}, {"_cpp11test_protect_many_cpp11_", (DL_FUNC) &_cpp11test_protect_many_cpp11_, 1}, {"_cpp11test_protect_many_preserve_", (DL_FUNC) &_cpp11test_protect_many_preserve_, 1}, diff --git a/cpp11test/src/map.cpp b/cpp11test/src/map.cpp index e4a32db4..71b3402d 100644 --- a/cpp11test/src/map.cpp +++ b/cpp11test/src/map.cpp @@ -10,6 +10,15 @@ return cpp11::as_sexp(counts); } +[[cpp11::register]] SEXP ordered_map_to_list_2_(cpp11::doubles x) { + std::map counts; + double n = x.size(); + for (int i = 0; i < n; i++) { + counts[x[i]] += 1.0; + } + return cpp11::as_sexp(counts); +} + [[cpp11::register]] SEXP unordered_map_to_list_(cpp11::doubles x) { std::unordered_map counts; int n = x.size(); diff --git a/cpp11test/tests/testthat/test-map-to-list.R b/cpp11test/tests/testthat/test-map-to-list.R index dc637afa..dd7fd5ca 100644 --- a/cpp11test/tests/testthat/test-map-to-list.R +++ b/cpp11test/tests/testthat/test-map-to-list.R @@ -6,6 +6,9 @@ test_that("ordered and unordered C++ maps are converted to R lists", { om <- ordered_map_to_list_(x) expect_type(om, "list") + om2 <- ordered_map_to_list_2_(x) + expect_equal(om, om2) + om_doubles <- as.double(names(om)) expect_equal(om_doubles, sort(om_doubles)) diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 374c0518..e7bb9b71 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -336,33 +336,18 @@ enable_if_convertible_to_sexp as_sexp(const T& from) { return from; } -// Pacha: Specialization for std::map -// NOTE: I did not use templates to avoid clashes with doubles/function/etc. -inline SEXP as_sexp(const std::map& map) { - R_xlen_t size = map.size(); - SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); - SEXP names = PROTECT(Rf_allocVector(STRSXP, size)); - - auto it = map.begin(); - for (R_xlen_t i = 0; i < size; ++i, ++it) { - SET_VECTOR_ELT(result, i, it->second); - SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8)); - } - - Rf_setAttrib(result, R_NamesSymbol, names); - UNPROTECT(2); - return result; -} - -// Specialization for std::map -inline SEXP as_sexp(const std::map& map) { +// Templated specialization for std::map +template ::value && + std::is_arithmetic::value>> +inline SEXP as_sexp(const std::map& map) { R_xlen_t size = map.size(); SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); SEXP names = PROTECT(Rf_allocVector(REALSXP, size)); auto it = map.begin(); for (R_xlen_t i = 0; i < size; ++i, ++it) { - SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second)); + SET_VECTOR_ELT(result, i, as_sexp(it->second)); REAL(names)[i] = it->first; } @@ -371,32 +356,18 @@ inline SEXP as_sexp(const std::map& map) { return result; } -// Pacha: Specialization for std::unordered_map -inline SEXP as_sexp(const std::unordered_map& map) { - R_xlen_t size = map.size(); - SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); - SEXP names = PROTECT(Rf_allocVector(STRSXP, size)); - - auto it = map.begin(); - for (R_xlen_t i = 0; i < size; ++i, ++it) { - SET_VECTOR_ELT(result, i, it->second); - SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8)); - } - - Rf_setAttrib(result, R_NamesSymbol, names); - UNPROTECT(2); - return result; -} - -// Specialization for std::unordered_map -inline SEXP as_sexp(const std::unordered_map& map) { +// Templated specialization for std::unordered_map +template ::value && + std::is_arithmetic::value>> +inline SEXP as_sexp(const std::unordered_map& map) { R_xlen_t size = map.size(); SEXP result = PROTECT(Rf_allocVector(VECSXP, size)); SEXP names = PROTECT(Rf_allocVector(REALSXP, size)); auto it = map.begin(); for (R_xlen_t i = 0; i < size; ++i, ++it) { - SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second)); + SET_VECTOR_ELT(result, i, as_sexp(it->second)); REAL(names)[i] = it->first; } From 9ebcf417d5ff771f50a9131c5382b757c0cbeb04 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 3 Jan 2025 10:20:48 +0000 Subject: [PATCH 11/46] fix sum rcpp error on Unix systems (cpp11test) --- cpp11test/.Rbuildignore | 3 +++ cpp11test/DESCRIPTION | 8 +++++--- cpp11test/LICENSE | 2 ++ cpp11test/LICENSE.md | 21 +++++++++++++++++++++ cpp11test/NAMESPACE | 1 + cpp11test/R/cpp11test-package.R | 2 ++ cpp11test/R/test.R | 3 +-- cpp11test/man/cpp11test-package.Rd | 20 -------------------- cpp11test/tests/testthat.R | 10 +++++++++- 9 files changed, 44 insertions(+), 26 deletions(-) create mode 100644 cpp11test/.Rbuildignore create mode 100644 cpp11test/LICENSE create mode 100644 cpp11test/LICENSE.md delete mode 100644 cpp11test/man/cpp11test-package.Rd diff --git a/cpp11test/.Rbuildignore b/cpp11test/.Rbuildignore new file mode 100644 index 00000000..1fe2b254 --- /dev/null +++ b/cpp11test/.Rbuildignore @@ -0,0 +1,3 @@ +^\.here$ +^bench$ +^LICENSE\.md$ diff --git a/cpp11test/DESCRIPTION b/cpp11test/DESCRIPTION index d1d05665..07d0a547 100644 --- a/cpp11test/DESCRIPTION +++ b/cpp11test/DESCRIPTION @@ -12,12 +12,14 @@ Authors@R: Description: Provides a test suite and benchmarking code for the 'cpp11' package. License: MIT + file LICENSE Encoding: UTF-8 -LinkingTo: Rcpp, cpp11, testthat +LinkingTo: cpp11, Rcpp, testthat +Imports: cpp11, Rcpp Suggests: covr, - testthat, + testthat (>= 3.0.0), pkgload, xml2 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.2 +Config/testthat/edition: 3 diff --git a/cpp11test/LICENSE b/cpp11test/LICENSE new file mode 100644 index 00000000..13458bda --- /dev/null +++ b/cpp11test/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2025 +COPYRIGHT HOLDER: cpp11test authors diff --git a/cpp11test/LICENSE.md b/cpp11test/LICENSE.md new file mode 100644 index 00000000..9350bfc6 --- /dev/null +++ b/cpp11test/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2025 cpp11test authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cpp11test/NAMESPACE b/cpp11test/NAMESPACE index 0cb4a22d..31d989df 100644 --- a/cpp11test/NAMESPACE +++ b/cpp11test/NAMESPACE @@ -3,4 +3,5 @@ export(run_tests) exportPattern("_$") importFrom(Rcpp,sourceCpp) +importFrom(cpp11,cpp_source) useDynLib(cpp11test, .registration = TRUE) diff --git a/cpp11test/R/cpp11test-package.R b/cpp11test/R/cpp11test-package.R index 16700446..e606536b 100644 --- a/cpp11test/R/cpp11test-package.R +++ b/cpp11test/R/cpp11test-package.R @@ -1,6 +1,8 @@ #' @keywords internal #' @exportPattern "_$" +#' @importFrom cpp11 cpp_source #' @importFrom Rcpp sourceCpp +NULL "_PACKAGE" # The following block is used by usethis to automatically manage diff --git a/cpp11test/R/test.R b/cpp11test/R/test.R index 07c0ff4d..618bd726 100644 --- a/cpp11test/R/test.R +++ b/cpp11test/R/test.R @@ -22,6 +22,5 @@ run_tests <- function(reporter = testthat::default_reporter()) { on.exit(setwd(old)) setwd(system.file("tests", package = "cpp11test")) - library(testthat) - test_check("cpp11test", reporter = reporter) + testthat::test_check("cpp11test", reporter = reporter) } diff --git a/cpp11test/man/cpp11test-package.Rd b/cpp11test/man/cpp11test-package.Rd deleted file mode 100644 index b0d9e562..00000000 --- a/cpp11test/man/cpp11test-package.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cpp11test-package.R -\docType{package} -\name{cpp11test-package} -\alias{cpp11test} -\alias{cpp11test-package} -\title{cpp11test: A test suite and benchmark code for 'cpp11'} -\description{ -Provides a test suite and benchmarking code for the 'cpp11' package. -} -\author{ -\strong{Maintainer}: Jim Hester \email{james.f.hester@gmail.com} (\href{https://orcid.org/0000-0002-2739-7082}{ORCID}) - -Other contributors: -\itemize{ - \item RStudio [copyright holder, funder] -} - -} -\keyword{internal} diff --git a/cpp11test/tests/testthat.R b/cpp11test/tests/testthat.R index 366c334f..c9dc587d 100644 --- a/cpp11test/tests/testthat.R +++ b/cpp11test/tests/testthat.R @@ -1,4 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + library(testthat) -library(cpp11) +library(cpp11test) test_check("cpp11test") From fb3583fac5d523f362d70b78cb67939a6edec2ee Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 3 Jan 2025 13:23:11 +0000 Subject: [PATCH 12/46] roxygen comments on cpp side (works ok with 1 roxygenised function per script for now) --- R/register.R | 34 ++++++--- cpp11test/DESCRIPTION | 2 +- cpp11test/R/cpp11.R | 160 ++++++++++++++++++++++++---------------- cpp11test/src/cpp11.cpp | 32 ++++++++ 4 files changed, 155 insertions(+), 73 deletions(-) diff --git a/R/register.R b/R/register.R index 3ea10d52..fe49f749 100644 --- a/R/register.R +++ b/R/register.R @@ -77,7 +77,6 @@ cpp_register <- function(path = ".", quiet = !is_interactive(), extension = c(". cli::cli_alert_success("generated file {.file {basename(r_path)}}") } - call_entries <- get_call_entries(path, funs$name, package) cpp_function_registration <- glue::glue_data(funs, ' {{ @@ -85,9 +84,9 @@ cpp_register <- function(path = ".", quiet = !is_interactive(), extension = c(". n_args = viapply(funs$args, nrow) ) - cpp_function_registration <- glue::glue_collapse(cpp_function_registration, sep = "\n") + cpp_function_registration <- glue::glue_collapse(cpp_function_registration, sep = "\n") - extra_includes <- character() + extra_includes <- character() if (pkg_links_to_rcpp(path)) { extra_includes <- c(extra_includes, "#include ", "#include ", "using namespace Rcpp;") } @@ -215,13 +214,13 @@ generate_init_functions <- function(funs) { } generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { - funs <- funs[c("name", "return_type", "args")] + funs <- funs[c("name", "return_type", "args", "file", "line", "decoration")] if (use_package) { package_call <- glue::glue(', PACKAGE = "{package}"') package_names <- glue::glue_data(funs, '"_{package}_{name}"') } else { - package_names <- glue::glue_data(funs, '`_{package}_{name}`') + package_names <- glue::glue_data(funs, "`_{package}_{name}`") package_call <- "" } @@ -235,15 +234,30 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { glue::glue_data(funs, '.Call({package_names}{params}{package_call})') ) - out <- glue::glue_data(funs, ' - {name} <- function({list_params}) {{ - {calls} - }} - ') + roxygen_comments <- lapply(funs$file, extract_roxygen_comments) + + out <- mapply(function(name, list_params, calls, roxygen_comment) { + glue::glue("{if (nzchar(roxygen_comment)) paste0(roxygen_comment, '\n') else ''}{name} <- function({list_params}) {{\n\t{calls}\n}}") + }, funs$name, funs$list_params, funs$calls, roxygen_comments, SIMPLIFY = TRUE) + out <- glue::trim(out) out <- glue::glue_collapse(out, sep = "\n\n") unclass(out) } +extract_roxygen_comments <- function(file) { + lines <- readLines(file) + roxygen_start <- grep("^/\\* roxygen start", lines) + roxygen_end <- grep("roxygen end \\*/$", lines) + + if (length(roxygen_start) == 0 || length(roxygen_end) == 0) { + return("") + } + + roxygen_lines <- lines[(roxygen_start + 1):(roxygen_end - 1)] + roxygen_lines <- sub("^@", "#' @", roxygen_lines) + paste(roxygen_lines, collapse = "\n") +} + wrap_call <- function(name, return_type, args) { call <- glue::glue('{name}({list_params})', list_params = glue_collapse_data(args, "cpp11::as_cpp>({name})")) if (return_type == "void") { diff --git a/cpp11test/DESCRIPTION b/cpp11test/DESCRIPTION index d1d05665..70c5649f 100644 --- a/cpp11test/DESCRIPTION +++ b/cpp11test/DESCRIPTION @@ -20,4 +20,4 @@ Suggests: xml2 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.2 diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 038e7b76..3a4b1a2b 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -1,249 +1,285 @@ # Generated by cpp11: do not edit by hand cpp11_add_vec_for_ <- function(x, num) { - .Call(`_cpp11test_cpp11_add_vec_for_`, x, num) + .Call(`_cpp11test_cpp11_add_vec_for_`, x, num) } data_frame_ <- function() { - .Call(`_cpp11test_data_frame_`) + .Call(`_cpp11test_data_frame_`) } my_stop_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_stop_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_stop_n1fmt`, mystring)) } my_stop_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_stop_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_stop_n2fmt`, mystring, myarg)) } my_warning_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_warning_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_warning_n1fmt`, mystring)) } my_warning_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_warning_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_warning_n2fmt`, mystring, myarg)) } my_message_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_message_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_message_n1fmt`, mystring)) } my_message_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_message_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_message_n2fmt`, mystring, myarg)) } my_stop <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_stop`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_stop`, mystring, myarg)) } my_stop_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_stop_n1`, mystring)) + invisible(.Call(`_cpp11test_my_stop_n1`, mystring)) } my_warning <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_warning`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_warning`, mystring, myarg)) } my_warning_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_warning_n1`, mystring)) + invisible(.Call(`_cpp11test_my_warning_n1`, mystring)) } my_message <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_message`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_message`, mystring, myarg)) } my_message_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_message_n1`, mystring)) + invisible(.Call(`_cpp11test_my_message_n1`, mystring)) } remove_altrep <- function(x) { - .Call(`_cpp11test_remove_altrep`, x) + .Call(`_cpp11test_remove_altrep`, x) } upper_bound <- function(x, breaks) { - .Call(`_cpp11test_upper_bound`, x, breaks) + .Call(`_cpp11test_upper_bound`, x, breaks) } findInterval2 <- function(x, breaks) { - .Call(`_cpp11test_findInterval2`, x, breaks) + .Call(`_cpp11test_findInterval2`, x, breaks) } findInterval2_5 <- function(x, breaks) { - .Call(`_cpp11test_findInterval2_5`, x, breaks) + .Call(`_cpp11test_findInterval2_5`, x, breaks) } findInterval3 <- function(x, breaks) { - .Call(`_cpp11test_findInterval3`, x, breaks) + .Call(`_cpp11test_findInterval3`, x, breaks) } findInterval4 <- function(x, breaks) { - .Call(`_cpp11test_findInterval4`, x, breaks) + .Call(`_cpp11test_findInterval4`, x, breaks) } grow_ <- function(n) { - .Call(`_cpp11test_grow_`, n) + .Call(`_cpp11test_grow_`, n) } cpp11_insert_ <- function(num_sxp) { - .Call(`_cpp11test_cpp11_insert_`, num_sxp) + .Call(`_cpp11test_cpp11_insert_`, num_sxp) } gibbs_cpp <- function(N, thin) { - .Call(`_cpp11test_gibbs_cpp`, N, thin) + .Call(`_cpp11test_gibbs_cpp`, N, thin) } gibbs_cpp2 <- function(N, thin) { - .Call(`_cpp11test_gibbs_cpp2`, N, thin) + .Call(`_cpp11test_gibbs_cpp2`, N, thin) } gibbs_rcpp <- function(N, thin) { - .Call(`_cpp11test_gibbs_rcpp`, N, thin) + .Call(`_cpp11test_gibbs_rcpp`, N, thin) } gibbs_rcpp2 <- function(N, thin) { - .Call(`_cpp11test_gibbs_rcpp2`, N, thin) + .Call(`_cpp11test_gibbs_rcpp2`, N, thin) } row_sums <- function(x) { - .Call(`_cpp11test_row_sums`, x) + .Call(`_cpp11test_row_sums`, x) } col_sums <- function(x) { - .Call(`_cpp11test_col_sums`, x) + .Call(`_cpp11test_col_sums`, x) } protect_one_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_`, x, n)) } protect_one_sexp_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_sexp_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_sexp_`, x, n)) } protect_one_cpp11_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_cpp11_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_cpp11_`, x, n)) } protect_one_preserve_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_preserve_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_preserve_`, x, n)) } protect_many_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_`, n)) + invisible(.Call(`_cpp11test_protect_many_`, n)) } protect_many_cpp11_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_cpp11_`, n)) + invisible(.Call(`_cpp11test_protect_many_cpp11_`, n)) } protect_many_sexp_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_sexp_`, n)) + invisible(.Call(`_cpp11test_protect_many_sexp_`, n)) } protect_many_preserve_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_preserve_`, n)) + invisible(.Call(`_cpp11test_protect_many_preserve_`, n)) } protect_many_rcpp_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_rcpp_`, n)) + invisible(.Call(`_cpp11test_protect_many_rcpp_`, n)) } cpp11_release_ <- function(n) { - invisible(.Call(`_cpp11test_cpp11_release_`, n)) + invisible(.Call(`_cpp11test_cpp11_release_`, n)) } rcpp_release_ <- function(n) { - invisible(.Call(`_cpp11test_rcpp_release_`, n)) + invisible(.Call(`_cpp11test_rcpp_release_`, n)) +} + +#' @title Roxygenise C++ function +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @export +#' @examples roxcpp_(1.0) +roxcpp1_ <- function(x) { + .Call(`_cpp11test_roxcpp1_`, x) +} + +#' @title Roxygenise C++ function +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @export +#' @examples roxcpp_(1.0) +roxcpp2_ <- function(x) { + .Call(`_cpp11test_roxcpp2_`, x) +} + +#' @title Roxygenise C++ function +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @export +#' @examples roxcpp_(1.0) +roxcpp3_ <- function(x) { + .Call(`_cpp11test_roxcpp3_`, x) +} + +#' @title Roxygenise C++ function +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @export +#' @examples roxcpp_(1.0) +roxcpp4_ <- function(x) { + .Call(`_cpp11test_roxcpp4_`, x) } cpp11_safe_ <- function(x_sxp) { - .Call(`_cpp11test_cpp11_safe_`, x_sxp) + .Call(`_cpp11test_cpp11_safe_`, x_sxp) } string_proxy_assignment_ <- function() { - .Call(`_cpp11test_string_proxy_assignment_`) + .Call(`_cpp11test_string_proxy_assignment_`) } string_push_back_ <- function() { - .Call(`_cpp11test_string_push_back_`) + .Call(`_cpp11test_string_push_back_`) } sum_dbl_for_ <- function(x) { - .Call(`_cpp11test_sum_dbl_for_`, x) + .Call(`_cpp11test_sum_dbl_for_`, x) } sum_dbl_for2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_for2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_for2_`, x_sxp) } sum_dbl_for3_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_for3_`, x_sxp) + .Call(`_cpp11test_sum_dbl_for3_`, x_sxp) } sum_dbl_foreach_ <- function(x) { - .Call(`_cpp11test_sum_dbl_foreach_`, x) + .Call(`_cpp11test_sum_dbl_foreach_`, x) } sum_dbl_foreach2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_foreach2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_foreach2_`, x_sxp) } sum_dbl_accumulate_ <- function(x) { - .Call(`_cpp11test_sum_dbl_accumulate_`, x) + .Call(`_cpp11test_sum_dbl_accumulate_`, x) } sum_dbl_accumulate2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) } sum_int_for_ <- function(x) { - .Call(`_cpp11test_sum_int_for_`, x) + .Call(`_cpp11test_sum_int_for_`, x) } sum_int_for2_ <- function(x_) { - .Call(`_cpp11test_sum_int_for2_`, x_) + .Call(`_cpp11test_sum_int_for2_`, x_) } sum_int_foreach_ <- function(x) { - .Call(`_cpp11test_sum_int_foreach_`, x) + .Call(`_cpp11test_sum_int_foreach_`, x) } sum_int_accumulate_ <- function(x) { - .Call(`_cpp11test_sum_int_accumulate_`, x) + .Call(`_cpp11test_sum_int_accumulate_`, x) } rcpp_sum_dbl_for_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_for_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_for_`, x_sxp) } rcpp_sum_int_for_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_int_for_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_int_for_`, x_sxp) } rcpp_sum_dbl_foreach_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_foreach_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_foreach_`, x_sxp) } rcpp_sum_dbl_accumulate_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_accumulate_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_accumulate_`, x_sxp) } rcpp_grow_ <- function(n_sxp) { - .Call(`_cpp11test_rcpp_grow_`, n_sxp) + .Call(`_cpp11test_rcpp_grow_`, n_sxp) } rcpp_push_and_truncate_ <- function(size_sxp) { - .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) + .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) } test_destruction_inner <- function() { - invisible(.Call(`_cpp11test_test_destruction_inner`)) + invisible(.Call(`_cpp11test_test_destruction_inner`)) } test_destruction_outer <- function() { - invisible(.Call(`_cpp11test_test_destruction_outer`)) + invisible(.Call(`_cpp11test_test_destruction_outer`)) } cpp11_push_and_truncate_ <- function(size_sexp) { - .Call(`_cpp11test_cpp11_push_and_truncate_`, size_sexp) + .Call(`_cpp11test_cpp11_push_and_truncate_`, size_sexp) } diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 421de637..55e48fbc 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -303,6 +303,34 @@ extern "C" SEXP _cpp11test_rcpp_release_(SEXP n) { return R_NilValue; END_CPP11 } +// roxygen1.cpp +double roxcpp1_(double x); +extern "C" SEXP _cpp11test_roxcpp1_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp1_(cpp11::as_cpp>(x))); + END_CPP11 +} +// roxygen1.cpp +double roxcpp2_(double x); +extern "C" SEXP _cpp11test_roxcpp2_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp2_(cpp11::as_cpp>(x))); + END_CPP11 +} +// roxygen2.cpp +double roxcpp3_(double x); +extern "C" SEXP _cpp11test_roxcpp3_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp3_(cpp11::as_cpp>(x))); + END_CPP11 +} +// roxygen2.cpp +double roxcpp4_(double x); +extern "C" SEXP _cpp11test_roxcpp4_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp4_(cpp11::as_cpp>(x))); + END_CPP11 +} // safe.cpp SEXP cpp11_safe_(SEXP x_sxp); extern "C" SEXP _cpp11test_cpp11_safe_(SEXP x_sxp) { @@ -518,6 +546,10 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_rcpp_sum_int_for_", (DL_FUNC) &_cpp11test_rcpp_sum_int_for_, 1}, {"_cpp11test_remove_altrep", (DL_FUNC) &_cpp11test_remove_altrep, 1}, {"_cpp11test_row_sums", (DL_FUNC) &_cpp11test_row_sums, 1}, + {"_cpp11test_roxcpp1_", (DL_FUNC) &_cpp11test_roxcpp1_, 1}, + {"_cpp11test_roxcpp2_", (DL_FUNC) &_cpp11test_roxcpp2_, 1}, + {"_cpp11test_roxcpp3_", (DL_FUNC) &_cpp11test_roxcpp3_, 1}, + {"_cpp11test_roxcpp4_", (DL_FUNC) &_cpp11test_roxcpp4_, 1}, {"_cpp11test_string_proxy_assignment_", (DL_FUNC) &_cpp11test_string_proxy_assignment_, 0}, {"_cpp11test_string_push_back_", (DL_FUNC) &_cpp11test_string_push_back_, 0}, {"_cpp11test_sum_dbl_accumulate2_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate2_, 1}, From 14531dbcd16434838c475b2bf435dfc376494b0d Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 04:10:39 +0000 Subject: [PATCH 13/46] almost there with rx/no rx in the same file --- R/register.R | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/R/register.R b/R/register.R index fe49f749..10ff5cdb 100644 --- a/R/register.R +++ b/R/register.R @@ -224,21 +224,33 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { package_call <- "" } - funs$package <- package funs$package_call <- package_call funs$list_params <- vcapply(funs$args, glue_collapse_data, "{name}") funs$params <- vcapply(funs$list_params, function(x) if (nzchar(x)) paste0(", ", x) else x) is_void <- funs$return_type == "void" funs$calls <- ifelse(is_void, - glue::glue_data(funs, 'invisible(.Call({package_names}{params}{package_call}))'), - glue::glue_data(funs, '.Call({package_names}{params}{package_call})') + glue::glue_data(funs, "invisible(.Call({package_names}{params}{package_call}))"), + glue::glue_data(funs, ".Call({package_names}{params}{package_call})") ) roxygen_comments <- lapply(funs$file, extract_roxygen_comments) - out <- mapply(function(name, list_params, calls, roxygen_comment) { - glue::glue("{if (nzchar(roxygen_comment)) paste0(roxygen_comment, '\n') else ''}{name} <- function({list_params}) {{\n\t{calls}\n}}") - }, funs$name, funs$list_params, funs$calls, roxygen_comments, SIMPLIFY = TRUE) + out <- mapply(function(name, list_params, calls, file, line) { + comments <- extract_roxygen_comments(file) + roxygen_comment <- "" + for (comment in comments) { + if (comment$line < line) { + roxygen_comment <- comment$text + } + } + if (nzchar(roxygen_comment)) { + glue::glue("{roxygen_comment}\n{name} <- function({list_params}) {{\n\t{calls}\n}}") + } else { + glue::glue("{name} <- function({list_params}) {{\n\t{calls}\n}}") + } + }, funs$name, funs$list_params, funs$calls, funs$file, funs$line, SIMPLIFY = FALSE) + + out <- as.character(out) out <- glue::trim(out) out <- glue::glue_collapse(out, sep = "\n\n") unclass(out) @@ -250,12 +262,16 @@ extract_roxygen_comments <- function(file) { roxygen_end <- grep("roxygen end \\*/$", lines) if (length(roxygen_start) == 0 || length(roxygen_end) == 0) { - return("") + return(list()) } - roxygen_lines <- lines[(roxygen_start + 1):(roxygen_end - 1)] - roxygen_lines <- sub("^@", "#' @", roxygen_lines) - paste(roxygen_lines, collapse = "\n") + roxygen_comments <- mapply(function(start, end) { + roxygen_lines <- lines[(start + 1):(end - 1)] + roxygen_lines <- sub("^@", "#' @", roxygen_lines) + list(line = start, text = paste(roxygen_lines, collapse = "\n")) + }, roxygen_start, roxygen_end, SIMPLIFY = FALSE) + + roxygen_comments } wrap_call <- function(name, return_type, args) { From c750115870ec8a12de191414e53b70611c081703 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 04:56:13 +0000 Subject: [PATCH 14/46] correctly handles roxygen in cpp files --- R/register.R | 22 ++++++++++------- cpp11test/NAMESPACE | 5 ++++ cpp11test/R/cpp11.R | 49 +++++++++++++++++++++++++------------- cpp11test/man/roxcpp2_.Rd | 17 +++++++++++++ cpp11test/man/roxcpp3_.Rd | 17 +++++++++++++ cpp11test/man/roxcpp4_.Rd | 17 +++++++++++++ cpp11test/man/roxcpp5_.Rd | 17 +++++++++++++ cpp11test/man/roxcpp7_.Rd | 17 +++++++++++++ cpp11test/src/cpp11.cpp | 32 +++++++++++++++++++++---- cpp11test/src/roxygen1.cpp | 22 +++++++++++++++++ cpp11test/src/roxygen2.cpp | 28 ++++++++++++++++++++++ cpp11test/src/roxygen3.cpp | 34 ++++++++++++++++++++++++++ 12 files changed, 249 insertions(+), 28 deletions(-) create mode 100644 cpp11test/man/roxcpp2_.Rd create mode 100644 cpp11test/man/roxcpp3_.Rd create mode 100644 cpp11test/man/roxcpp4_.Rd create mode 100644 cpp11test/man/roxcpp5_.Rd create mode 100644 cpp11test/man/roxcpp7_.Rd create mode 100644 cpp11test/src/roxygen1.cpp create mode 100644 cpp11test/src/roxygen2.cpp create mode 100644 cpp11test/src/roxygen3.cpp diff --git a/R/register.R b/R/register.R index 10ff5cdb..cec5661c 100644 --- a/R/register.R +++ b/R/register.R @@ -233,22 +233,28 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { glue::glue_data(funs, ".Call({package_names}{params}{package_call})") ) - roxygen_comments <- lapply(funs$file, extract_roxygen_comments) - - out <- mapply(function(name, list_params, calls, file, line) { + # Parse and associate Roxygen comments + funs$roxygen_comment <- mapply(function(file, line) { comments <- extract_roxygen_comments(file) - roxygen_comment <- "" + matched_comment <- "" for (comment in comments) { - if (comment$line < line) { - roxygen_comment <- comment$text + # Check if the comment directly precedes the function without gaps + if (line == comment$line + 1) { + matched_comment <- comment$text + break } } + matched_comment + }, funs$file, funs$line, SIMPLIFY = TRUE) + + # Generate R functions with or without Roxygen comments + out <- mapply(function(name, list_params, calls, roxygen_comment) { if (nzchar(roxygen_comment)) { glue::glue("{roxygen_comment}\n{name} <- function({list_params}) {{\n\t{calls}\n}}") } else { glue::glue("{name} <- function({list_params}) {{\n\t{calls}\n}}") } - }, funs$name, funs$list_params, funs$calls, funs$file, funs$line, SIMPLIFY = FALSE) + }, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, SIMPLIFY = FALSE) out <- as.character(out) out <- glue::trim(out) @@ -268,7 +274,7 @@ extract_roxygen_comments <- function(file) { roxygen_comments <- mapply(function(start, end) { roxygen_lines <- lines[(start + 1):(end - 1)] roxygen_lines <- sub("^@", "#' @", roxygen_lines) - list(line = start, text = paste(roxygen_lines, collapse = "\n")) + list(line = end, text = paste(roxygen_lines, collapse = "\n")) }, roxygen_start, roxygen_end, SIMPLIFY = FALSE) roxygen_comments diff --git a/cpp11test/NAMESPACE b/cpp11test/NAMESPACE index 0cb4a22d..90d018a1 100644 --- a/cpp11test/NAMESPACE +++ b/cpp11test/NAMESPACE @@ -1,5 +1,10 @@ # Generated by roxygen2: do not edit by hand +export(roxcpp2_) +export(roxcpp3_) +export(roxcpp4_) +export(roxcpp5_) +export(roxcpp7_) export(run_tests) exportPattern("_$") importFrom(Rcpp,sourceCpp) diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 3a4b1a2b..07a12506 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -156,42 +156,59 @@ rcpp_release_ <- function(n) { invisible(.Call(`_cpp11test_rcpp_release_`, n)) } -#' @title Roxygenise C++ function -#' @param x numeric value -#' @description Dummy function to test roxygen2. It adds 1.0 to a double. -#' @export -#' @examples roxcpp_(1.0) -roxcpp1_ <- function(x) { - .Call(`_cpp11test_roxcpp1_`, x) +notroxcpp1_ <- function(x) { + .Call(`_cpp11test_notroxcpp1_`, x) } -#' @title Roxygenise C++ function +#' @title Roxygenise C++ function II #' @param x numeric value -#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @description Dummy function to test roxygen2. It adds 2.0 to a double. #' @export -#' @examples roxcpp_(1.0) +#' @examples roxcpp2_(1.0) roxcpp2_ <- function(x) { .Call(`_cpp11test_roxcpp2_`, x) } -#' @title Roxygenise C++ function +#' @title Roxygenise C++ function III #' @param x numeric value -#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @description Dummy function to test roxygen2. It adds 3.0 to a double. #' @export -#' @examples roxcpp_(1.0) +#' @examples roxcpp3_(1.0) roxcpp3_ <- function(x) { .Call(`_cpp11test_roxcpp3_`, x) } -#' @title Roxygenise C++ function +#' @title Roxygenise C++ function IV #' @param x numeric value -#' @description Dummy function to test roxygen2. It adds 1.0 to a double. +#' @description Dummy function to test roxygen2. It adds 4.0 to a double. #' @export -#' @examples roxcpp_(1.0) +#' @examples roxcpp4_(1.0) roxcpp4_ <- function(x) { .Call(`_cpp11test_roxcpp4_`, x) } +#' @title Roxygenise C++ function V +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 5.0 to a double. +#' @export +#' @examples roxcpp5_(1.0) +roxcpp5_ <- function(x) { + .Call(`_cpp11test_roxcpp5_`, x) +} + +notroxcpp6_ <- function(x) { + .Call(`_cpp11test_notroxcpp6_`, x) +} + +#' @title Roxygenise C++ function VII +#' @param x numeric value +#' @description Dummy function to test roxygen2. It adds 7.0 to a double. +#' @export +#' @examples roxcpp7_(1.0) +roxcpp7_ <- function(x) { + .Call(`_cpp11test_roxcpp7_`, x) +} + cpp11_safe_ <- function(x_sxp) { .Call(`_cpp11test_cpp11_safe_`, x_sxp) } diff --git a/cpp11test/man/roxcpp2_.Rd b/cpp11test/man/roxcpp2_.Rd new file mode 100644 index 00000000..dd000a0f --- /dev/null +++ b/cpp11test/man/roxcpp2_.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp11.R +\name{roxcpp2_} +\alias{roxcpp2_} +\title{Roxygenise C++ function II} +\usage{ +roxcpp2_(x) +} +\arguments{ +\item{x}{numeric value} +} +\description{ +Dummy function to test roxygen2. It adds 2.0 to a double. +} +\examples{ +roxcpp2_(1.0) +} diff --git a/cpp11test/man/roxcpp3_.Rd b/cpp11test/man/roxcpp3_.Rd new file mode 100644 index 00000000..3d31d143 --- /dev/null +++ b/cpp11test/man/roxcpp3_.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp11.R +\name{roxcpp3_} +\alias{roxcpp3_} +\title{Roxygenise C++ function III} +\usage{ +roxcpp3_(x) +} +\arguments{ +\item{x}{numeric value} +} +\description{ +Dummy function to test roxygen2. It adds 3.0 to a double. +} +\examples{ +roxcpp3_(1.0) +} diff --git a/cpp11test/man/roxcpp4_.Rd b/cpp11test/man/roxcpp4_.Rd new file mode 100644 index 00000000..f9cbb022 --- /dev/null +++ b/cpp11test/man/roxcpp4_.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp11.R +\name{roxcpp4_} +\alias{roxcpp4_} +\title{Roxygenise C++ function IV} +\usage{ +roxcpp4_(x) +} +\arguments{ +\item{x}{numeric value} +} +\description{ +Dummy function to test roxygen2. It adds 4.0 to a double. +} +\examples{ +roxcpp4_(1.0) +} diff --git a/cpp11test/man/roxcpp5_.Rd b/cpp11test/man/roxcpp5_.Rd new file mode 100644 index 00000000..ada8f9ee --- /dev/null +++ b/cpp11test/man/roxcpp5_.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp11.R +\name{roxcpp5_} +\alias{roxcpp5_} +\title{Roxygenise C++ function V} +\usage{ +roxcpp5_(x) +} +\arguments{ +\item{x}{numeric value} +} +\description{ +Dummy function to test roxygen2. It adds 5.0 to a double. +} +\examples{ +roxcpp5_(1.0) +} diff --git a/cpp11test/man/roxcpp7_.Rd b/cpp11test/man/roxcpp7_.Rd new file mode 100644 index 00000000..bf972d1b --- /dev/null +++ b/cpp11test/man/roxcpp7_.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cpp11.R +\name{roxcpp7_} +\alias{roxcpp7_} +\title{Roxygenise C++ function VII} +\usage{ +roxcpp7_(x) +} +\arguments{ +\item{x}{numeric value} +} +\description{ +Dummy function to test roxygen2. It adds 7.0 to a double. +} +\examples{ +roxcpp7_(1.0) +} diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 55e48fbc..4f4f84d0 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -304,10 +304,10 @@ extern "C" SEXP _cpp11test_rcpp_release_(SEXP n) { END_CPP11 } // roxygen1.cpp -double roxcpp1_(double x); -extern "C" SEXP _cpp11test_roxcpp1_(SEXP x) { +double notroxcpp1_(double x); +extern "C" SEXP _cpp11test_notroxcpp1_(SEXP x) { BEGIN_CPP11 - return cpp11::as_sexp(roxcpp1_(cpp11::as_cpp>(x))); + return cpp11::as_sexp(notroxcpp1_(cpp11::as_cpp>(x))); END_CPP11 } // roxygen1.cpp @@ -331,6 +331,27 @@ extern "C" SEXP _cpp11test_roxcpp4_(SEXP x) { return cpp11::as_sexp(roxcpp4_(cpp11::as_cpp>(x))); END_CPP11 } +// roxygen3.cpp +double roxcpp5_(double x); +extern "C" SEXP _cpp11test_roxcpp5_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp5_(cpp11::as_cpp>(x))); + END_CPP11 +} +// roxygen3.cpp +double notroxcpp6_(double x); +extern "C" SEXP _cpp11test_notroxcpp6_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(notroxcpp6_(cpp11::as_cpp>(x))); + END_CPP11 +} +// roxygen3.cpp +double roxcpp7_(double x); +extern "C" SEXP _cpp11test_roxcpp7_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(roxcpp7_(cpp11::as_cpp>(x))); + END_CPP11 +} // safe.cpp SEXP cpp11_safe_(SEXP x_sxp); extern "C" SEXP _cpp11test_cpp11_safe_(SEXP x_sxp) { @@ -528,6 +549,8 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_my_warning_n1", (DL_FUNC) &_cpp11test_my_warning_n1, 1}, {"_cpp11test_my_warning_n1fmt", (DL_FUNC) &_cpp11test_my_warning_n1fmt, 1}, {"_cpp11test_my_warning_n2fmt", (DL_FUNC) &_cpp11test_my_warning_n2fmt, 2}, + {"_cpp11test_notroxcpp1_", (DL_FUNC) &_cpp11test_notroxcpp1_, 1}, + {"_cpp11test_notroxcpp6_", (DL_FUNC) &_cpp11test_notroxcpp6_, 1}, {"_cpp11test_protect_many_", (DL_FUNC) &_cpp11test_protect_many_, 1}, {"_cpp11test_protect_many_cpp11_", (DL_FUNC) &_cpp11test_protect_many_cpp11_, 1}, {"_cpp11test_protect_many_preserve_", (DL_FUNC) &_cpp11test_protect_many_preserve_, 1}, @@ -546,10 +569,11 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_rcpp_sum_int_for_", (DL_FUNC) &_cpp11test_rcpp_sum_int_for_, 1}, {"_cpp11test_remove_altrep", (DL_FUNC) &_cpp11test_remove_altrep, 1}, {"_cpp11test_row_sums", (DL_FUNC) &_cpp11test_row_sums, 1}, - {"_cpp11test_roxcpp1_", (DL_FUNC) &_cpp11test_roxcpp1_, 1}, {"_cpp11test_roxcpp2_", (DL_FUNC) &_cpp11test_roxcpp2_, 1}, {"_cpp11test_roxcpp3_", (DL_FUNC) &_cpp11test_roxcpp3_, 1}, {"_cpp11test_roxcpp4_", (DL_FUNC) &_cpp11test_roxcpp4_, 1}, + {"_cpp11test_roxcpp5_", (DL_FUNC) &_cpp11test_roxcpp5_, 1}, + {"_cpp11test_roxcpp7_", (DL_FUNC) &_cpp11test_roxcpp7_, 1}, {"_cpp11test_string_proxy_assignment_", (DL_FUNC) &_cpp11test_string_proxy_assignment_, 0}, {"_cpp11test_string_push_back_", (DL_FUNC) &_cpp11test_string_push_back_, 0}, {"_cpp11test_sum_dbl_accumulate2_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate2_, 1}, diff --git a/cpp11test/src/roxygen1.cpp b/cpp11test/src/roxygen1.cpp new file mode 100644 index 00000000..6ce5dea8 --- /dev/null +++ b/cpp11test/src/roxygen1.cpp @@ -0,0 +1,22 @@ +#include "cpp11/doubles.hpp" +using namespace cpp11; + +// Test: not documented + documented + +// Not Roxygenised C++ function I +[[cpp11::register]] double notroxcpp1_(double x) { + double y = x + 1.0; + return y; +} + +/* roxygen start +@title Roxygenise C++ function II +@param x numeric value +@description Dummy function to test roxygen2. It adds 2.0 to a double. +@export +@examples roxcpp2_(1.0) +roxygen end */ +[[cpp11::register]] double roxcpp2_(double x) { + double y = x + 2.0; + return y; +} diff --git a/cpp11test/src/roxygen2.cpp b/cpp11test/src/roxygen2.cpp new file mode 100644 index 00000000..ecd50221 --- /dev/null +++ b/cpp11test/src/roxygen2.cpp @@ -0,0 +1,28 @@ +#include "cpp11/doubles.hpp" +using namespace cpp11; + +// Test: documented + documented + +/* roxygen start +@title Roxygenise C++ function III +@param x numeric value +@description Dummy function to test roxygen2. It adds 3.0 to a double. +@export +@examples roxcpp3_(1.0) +roxygen end */ +[[cpp11::register]] double roxcpp3_(double x) { + double y = x + 3.0; + return y; +} + +/* roxygen start +@title Roxygenise C++ function IV +@param x numeric value +@description Dummy function to test roxygen2. It adds 4.0 to a double. +@export +@examples roxcpp4_(1.0) +roxygen end */ +[[cpp11::register]] double roxcpp4_(double x) { + double y = x + 4.0; + return y; +} diff --git a/cpp11test/src/roxygen3.cpp b/cpp11test/src/roxygen3.cpp new file mode 100644 index 00000000..44be0173 --- /dev/null +++ b/cpp11test/src/roxygen3.cpp @@ -0,0 +1,34 @@ +#include "cpp11/doubles.hpp" +using namespace cpp11; + +// Test: documented + not documented + documented + +/* roxygen start +@title Roxygenise C++ function V +@param x numeric value +@description Dummy function to test roxygen2. It adds 5.0 to a double. +@export +@examples roxcpp5_(1.0) +roxygen end */ +[[cpp11::register]] double roxcpp5_(double x) { + double y = x + 5.0; + return y; +} + +// Not Roxygenised C++ function VI +[[cpp11::register]] double notroxcpp6_(double x) { + double y = x + 6.0; + return y; +} + +/* roxygen start +@title Roxygenise C++ function VII +@param x numeric value +@description Dummy function to test roxygen2. It adds 7.0 to a double. +@export +@examples roxcpp7_(1.0) +roxygen end */ +[[cpp11::register]] double roxcpp7_(double x) { + double y = x + 7.0; + return y; +} From fb848c652de0f9d05eaf056a1cdbad14dbfa4ad3 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 05:01:19 +0000 Subject: [PATCH 15/46] revert some styler changes to make the changes more clear --- R/register.R | 8 +-- cpp11test/R/cpp11.R | 128 ++++++++++++++++++++++---------------------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/R/register.R b/R/register.R index cec5661c..43610049 100644 --- a/R/register.R +++ b/R/register.R @@ -220,7 +220,7 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { package_call <- glue::glue(', PACKAGE = "{package}"') package_names <- glue::glue_data(funs, '"_{package}_{name}"') } else { - package_names <- glue::glue_data(funs, "`_{package}_{name}`") + package_names <- glue::glue_data(funs, '`_{package}_{name}`') package_call <- "" } @@ -229,8 +229,8 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { funs$params <- vcapply(funs$list_params, function(x) if (nzchar(x)) paste0(", ", x) else x) is_void <- funs$return_type == "void" funs$calls <- ifelse(is_void, - glue::glue_data(funs, "invisible(.Call({package_names}{params}{package_call}))"), - glue::glue_data(funs, ".Call({package_names}{params}{package_call})") + glue::glue_data(funs, 'invisible(.Call({package_names}{params}{package_call}))'), + glue::glue_data(funs, '.Call({package_names}{params}{package_call})') ) # Parse and associate Roxygen comments @@ -252,7 +252,7 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { if (nzchar(roxygen_comment)) { glue::glue("{roxygen_comment}\n{name} <- function({list_params}) {{\n\t{calls}\n}}") } else { - glue::glue("{name} <- function({list_params}) {{\n\t{calls}\n}}") + glue::glue("{name} <- function({list_params}) {{\n {calls}\n}}") } }, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, SIMPLIFY = FALSE) diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 07a12506..32e52ed8 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -1,163 +1,163 @@ # Generated by cpp11: do not edit by hand cpp11_add_vec_for_ <- function(x, num) { - .Call(`_cpp11test_cpp11_add_vec_for_`, x, num) + .Call(`_cpp11test_cpp11_add_vec_for_`, x, num) } data_frame_ <- function() { - .Call(`_cpp11test_data_frame_`) + .Call(`_cpp11test_data_frame_`) } my_stop_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_stop_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_stop_n1fmt`, mystring)) } my_stop_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_stop_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_stop_n2fmt`, mystring, myarg)) } my_warning_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_warning_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_warning_n1fmt`, mystring)) } my_warning_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_warning_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_warning_n2fmt`, mystring, myarg)) } my_message_n1fmt <- function(mystring) { - invisible(.Call(`_cpp11test_my_message_n1fmt`, mystring)) + invisible(.Call(`_cpp11test_my_message_n1fmt`, mystring)) } my_message_n2fmt <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_message_n2fmt`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_message_n2fmt`, mystring, myarg)) } my_stop <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_stop`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_stop`, mystring, myarg)) } my_stop_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_stop_n1`, mystring)) + invisible(.Call(`_cpp11test_my_stop_n1`, mystring)) } my_warning <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_warning`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_warning`, mystring, myarg)) } my_warning_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_warning_n1`, mystring)) + invisible(.Call(`_cpp11test_my_warning_n1`, mystring)) } my_message <- function(mystring, myarg) { - invisible(.Call(`_cpp11test_my_message`, mystring, myarg)) + invisible(.Call(`_cpp11test_my_message`, mystring, myarg)) } my_message_n1 <- function(mystring) { - invisible(.Call(`_cpp11test_my_message_n1`, mystring)) + invisible(.Call(`_cpp11test_my_message_n1`, mystring)) } remove_altrep <- function(x) { - .Call(`_cpp11test_remove_altrep`, x) + .Call(`_cpp11test_remove_altrep`, x) } upper_bound <- function(x, breaks) { - .Call(`_cpp11test_upper_bound`, x, breaks) + .Call(`_cpp11test_upper_bound`, x, breaks) } findInterval2 <- function(x, breaks) { - .Call(`_cpp11test_findInterval2`, x, breaks) + .Call(`_cpp11test_findInterval2`, x, breaks) } findInterval2_5 <- function(x, breaks) { - .Call(`_cpp11test_findInterval2_5`, x, breaks) + .Call(`_cpp11test_findInterval2_5`, x, breaks) } findInterval3 <- function(x, breaks) { - .Call(`_cpp11test_findInterval3`, x, breaks) + .Call(`_cpp11test_findInterval3`, x, breaks) } findInterval4 <- function(x, breaks) { - .Call(`_cpp11test_findInterval4`, x, breaks) + .Call(`_cpp11test_findInterval4`, x, breaks) } grow_ <- function(n) { - .Call(`_cpp11test_grow_`, n) + .Call(`_cpp11test_grow_`, n) } cpp11_insert_ <- function(num_sxp) { - .Call(`_cpp11test_cpp11_insert_`, num_sxp) + .Call(`_cpp11test_cpp11_insert_`, num_sxp) } gibbs_cpp <- function(N, thin) { - .Call(`_cpp11test_gibbs_cpp`, N, thin) + .Call(`_cpp11test_gibbs_cpp`, N, thin) } gibbs_cpp2 <- function(N, thin) { - .Call(`_cpp11test_gibbs_cpp2`, N, thin) + .Call(`_cpp11test_gibbs_cpp2`, N, thin) } gibbs_rcpp <- function(N, thin) { - .Call(`_cpp11test_gibbs_rcpp`, N, thin) + .Call(`_cpp11test_gibbs_rcpp`, N, thin) } gibbs_rcpp2 <- function(N, thin) { - .Call(`_cpp11test_gibbs_rcpp2`, N, thin) + .Call(`_cpp11test_gibbs_rcpp2`, N, thin) } row_sums <- function(x) { - .Call(`_cpp11test_row_sums`, x) + .Call(`_cpp11test_row_sums`, x) } col_sums <- function(x) { - .Call(`_cpp11test_col_sums`, x) + .Call(`_cpp11test_col_sums`, x) } protect_one_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_`, x, n)) } protect_one_sexp_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_sexp_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_sexp_`, x, n)) } protect_one_cpp11_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_cpp11_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_cpp11_`, x, n)) } protect_one_preserve_ <- function(x, n) { - invisible(.Call(`_cpp11test_protect_one_preserve_`, x, n)) + invisible(.Call(`_cpp11test_protect_one_preserve_`, x, n)) } protect_many_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_`, n)) + invisible(.Call(`_cpp11test_protect_many_`, n)) } protect_many_cpp11_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_cpp11_`, n)) + invisible(.Call(`_cpp11test_protect_many_cpp11_`, n)) } protect_many_sexp_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_sexp_`, n)) + invisible(.Call(`_cpp11test_protect_many_sexp_`, n)) } protect_many_preserve_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_preserve_`, n)) + invisible(.Call(`_cpp11test_protect_many_preserve_`, n)) } protect_many_rcpp_ <- function(n) { - invisible(.Call(`_cpp11test_protect_many_rcpp_`, n)) + invisible(.Call(`_cpp11test_protect_many_rcpp_`, n)) } cpp11_release_ <- function(n) { - invisible(.Call(`_cpp11test_cpp11_release_`, n)) + invisible(.Call(`_cpp11test_cpp11_release_`, n)) } rcpp_release_ <- function(n) { - invisible(.Call(`_cpp11test_rcpp_release_`, n)) + invisible(.Call(`_cpp11test_rcpp_release_`, n)) } notroxcpp1_ <- function(x) { - .Call(`_cpp11test_notroxcpp1_`, x) + .Call(`_cpp11test_notroxcpp1_`, x) } #' @title Roxygenise C++ function II @@ -197,7 +197,7 @@ roxcpp5_ <- function(x) { } notroxcpp6_ <- function(x) { - .Call(`_cpp11test_notroxcpp6_`, x) + .Call(`_cpp11test_notroxcpp6_`, x) } #' @title Roxygenise C++ function VII @@ -210,93 +210,93 @@ roxcpp7_ <- function(x) { } cpp11_safe_ <- function(x_sxp) { - .Call(`_cpp11test_cpp11_safe_`, x_sxp) + .Call(`_cpp11test_cpp11_safe_`, x_sxp) } string_proxy_assignment_ <- function() { - .Call(`_cpp11test_string_proxy_assignment_`) + .Call(`_cpp11test_string_proxy_assignment_`) } string_push_back_ <- function() { - .Call(`_cpp11test_string_push_back_`) + .Call(`_cpp11test_string_push_back_`) } sum_dbl_for_ <- function(x) { - .Call(`_cpp11test_sum_dbl_for_`, x) + .Call(`_cpp11test_sum_dbl_for_`, x) } sum_dbl_for2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_for2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_for2_`, x_sxp) } sum_dbl_for3_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_for3_`, x_sxp) + .Call(`_cpp11test_sum_dbl_for3_`, x_sxp) } sum_dbl_foreach_ <- function(x) { - .Call(`_cpp11test_sum_dbl_foreach_`, x) + .Call(`_cpp11test_sum_dbl_foreach_`, x) } sum_dbl_foreach2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_foreach2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_foreach2_`, x_sxp) } sum_dbl_accumulate_ <- function(x) { - .Call(`_cpp11test_sum_dbl_accumulate_`, x) + .Call(`_cpp11test_sum_dbl_accumulate_`, x) } sum_dbl_accumulate2_ <- function(x_sxp) { - .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) + .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) } sum_int_for_ <- function(x) { - .Call(`_cpp11test_sum_int_for_`, x) + .Call(`_cpp11test_sum_int_for_`, x) } sum_int_for2_ <- function(x_) { - .Call(`_cpp11test_sum_int_for2_`, x_) + .Call(`_cpp11test_sum_int_for2_`, x_) } sum_int_foreach_ <- function(x) { - .Call(`_cpp11test_sum_int_foreach_`, x) + .Call(`_cpp11test_sum_int_foreach_`, x) } sum_int_accumulate_ <- function(x) { - .Call(`_cpp11test_sum_int_accumulate_`, x) + .Call(`_cpp11test_sum_int_accumulate_`, x) } rcpp_sum_dbl_for_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_for_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_for_`, x_sxp) } rcpp_sum_int_for_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_int_for_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_int_for_`, x_sxp) } rcpp_sum_dbl_foreach_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_foreach_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_foreach_`, x_sxp) } rcpp_sum_dbl_accumulate_ <- function(x_sxp) { - .Call(`_cpp11test_rcpp_sum_dbl_accumulate_`, x_sxp) + .Call(`_cpp11test_rcpp_sum_dbl_accumulate_`, x_sxp) } rcpp_grow_ <- function(n_sxp) { - .Call(`_cpp11test_rcpp_grow_`, n_sxp) + .Call(`_cpp11test_rcpp_grow_`, n_sxp) } rcpp_push_and_truncate_ <- function(size_sxp) { - .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) + .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) } test_destruction_inner <- function() { - invisible(.Call(`_cpp11test_test_destruction_inner`)) + invisible(.Call(`_cpp11test_test_destruction_inner`)) } test_destruction_outer <- function() { - invisible(.Call(`_cpp11test_test_destruction_outer`)) + invisible(.Call(`_cpp11test_test_destruction_outer`)) } cpp11_push_and_truncate_ <- function(size_sexp) { - .Call(`_cpp11test_cpp11_push_and_truncate_`, size_sexp) + .Call(`_cpp11test_cpp11_push_and_truncate_`, size_sexp) } From 17cac60d615da82ff9f095d5901f6f10f47ecf09 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 08:09:14 +0000 Subject: [PATCH 16/46] fix multi line roxygen examples --- R/register.R | 2 +- cpp11test/R/cpp11.R | 6 +++++- cpp11test/man/roxcpp7_.Rd | 7 ++++++- cpp11test/src/roxygen3.cpp | 6 +++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/R/register.R b/R/register.R index 43610049..feec5450 100644 --- a/R/register.R +++ b/R/register.R @@ -273,7 +273,7 @@ extract_roxygen_comments <- function(file) { roxygen_comments <- mapply(function(start, end) { roxygen_lines <- lines[(start + 1):(end - 1)] - roxygen_lines <- sub("^@", "#' @", roxygen_lines) + roxygen_lines <- sub("^", "#' ", roxygen_lines) list(line = end, text = paste(roxygen_lines, collapse = "\n")) }, roxygen_start, roxygen_end, SIMPLIFY = FALSE) diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 32e52ed8..d5ae1ab2 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -204,7 +204,11 @@ notroxcpp6_ <- function(x) { #' @param x numeric value #' @description Dummy function to test roxygen2. It adds 7.0 to a double. #' @export -#' @examples roxcpp7_(1.0) +#' @examples +#' my_fun <- function(x) { +#' roxcpp7_(x) +#' } +#' @seealso \code{\link{roxcpp1_}} roxcpp7_ <- function(x) { .Call(`_cpp11test_roxcpp7_`, x) } diff --git a/cpp11test/man/roxcpp7_.Rd b/cpp11test/man/roxcpp7_.Rd index bf972d1b..17466bf6 100644 --- a/cpp11test/man/roxcpp7_.Rd +++ b/cpp11test/man/roxcpp7_.Rd @@ -13,5 +13,10 @@ roxcpp7_(x) Dummy function to test roxygen2. It adds 7.0 to a double. } \examples{ -roxcpp7_(1.0) +my_fun <- function(x) { + roxcpp7_(x) +} +} +\seealso{ +\code{\link{roxcpp1_}} } diff --git a/cpp11test/src/roxygen3.cpp b/cpp11test/src/roxygen3.cpp index 44be0173..7ede7a08 100644 --- a/cpp11test/src/roxygen3.cpp +++ b/cpp11test/src/roxygen3.cpp @@ -26,7 +26,11 @@ roxygen end */ @param x numeric value @description Dummy function to test roxygen2. It adds 7.0 to a double. @export -@examples roxcpp7_(1.0) +@examples +my_fun <- function(x) { + roxcpp7_(x) +} +@seealso \code{\link{roxcpp1_}} roxygen end */ [[cpp11::register]] double roxcpp7_(double x) { double y = x + 7.0; From 7a436d62b5d0714bede89af148217319e306b6a1 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 08:14:59 +0000 Subject: [PATCH 17/46] add roxygen example to documentation --- vignettes/converting.Rmd | 1 - vignettes/cpp11.Rmd | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/vignettes/converting.Rmd b/vignettes/converting.Rmd index 4a30dfd2..31507ca1 100644 --- a/vignettes/converting.Rmd +++ b/vignettes/converting.Rmd @@ -119,7 +119,6 @@ as_tibble(x, ".rows"_nm = num_rows, ".name_repair"_nm = name_repair); - Some parts of [Attributes](https://CRAN.R-project.org/package=Rcpp/vignettes/Rcpp-attributes.pdf) - No dependencies - No random number generator restoration - - No support for roxygen2 comments - No interfaces ### RNGs diff --git a/vignettes/cpp11.Rmd b/vignettes/cpp11.Rmd index 5f10fcc6..c23a2dbc 100644 --- a/vignettes/cpp11.Rmd +++ b/vignettes/cpp11.Rmd @@ -349,6 +349,34 @@ For the remainder of this vignette C++ code will be presented stand-alone rather If you want to try compiling and/or modifying the examples you should paste them into a C++ source file that includes the elements described above. This is easy to do in RMarkdown by using `{cpp11}` instead of `{r}` at the beginning of your code blocks. +## Roxygen support + +It is possible to use `roxygen2` to document your C++ functions. Here is an +example of how to do this: + +```{cpp11} +/* roxygen start +@title Mean of a numeric vector +@param x A numeric vector +@return The mean of the input vector +@examples mean_cpp(1:10) +@export +roxygen end */ +[[cpp11::register]] double mean_roxygenised_cpp(doubles x) { + int n = x.size(); + double total = 0; + for(double value : x) { + total += value; + } + return total / n; +} +``` + +Unlike R scripts, you need to use `/* roxygen start` and `roxygen end */` to +delimit the roxygen comments. The logic behind this is that C++ compilers +understand `/*` and `*/` as multi-linecomments, and therefore it is not required +to prepend `#' ` to each line of the roxygen comments as in R scripts. + ### Exercises 1. With the basics of C++ in hand, it's now a great time to practice by reading and writing some simple C++ functions. For each of the following functions, read the code and figure out what the corresponding base R function is. You might not understand every part of the code yet, but you should be able to figure out the basics of what the function does. From a781c99c5543e6f3220d8f7994cee47152946e56 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 12:09:54 +0000 Subject: [PATCH 18/46] consider the case where a file does not exist --- R/register.R | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/R/register.R b/R/register.R index feec5450..5022e801 100644 --- a/R/register.R +++ b/R/register.R @@ -220,7 +220,7 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { package_call <- glue::glue(', PACKAGE = "{package}"') package_names <- glue::glue_data(funs, '"_{package}_{name}"') } else { - package_names <- glue::glue_data(funs, '`_{package}_{name}`') + package_names <- glue::glue_data(funs, "`_{package}_{name}`") package_call <- "" } @@ -229,22 +229,26 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { funs$params <- vcapply(funs$list_params, function(x) if (nzchar(x)) paste0(", ", x) else x) is_void <- funs$return_type == "void" funs$calls <- ifelse(is_void, - glue::glue_data(funs, 'invisible(.Call({package_names}{params}{package_call}))'), - glue::glue_data(funs, '.Call({package_names}{params}{package_call})') + glue::glue_data(funs, "invisible(.Call({package_names}{params}{package_call}))"), + glue::glue_data(funs, ".Call({package_names}{params}{package_call})") ) # Parse and associate Roxygen comments funs$roxygen_comment <- mapply(function(file, line) { - comments <- extract_roxygen_comments(file) - matched_comment <- "" - for (comment in comments) { - # Check if the comment directly precedes the function without gaps - if (line == comment$line + 1) { - matched_comment <- comment$text - break + if (file.exists(file)) { + comments <- extract_roxygen_comments(file) + matched_comment <- "" + for (comment in comments) { + # Check if the comment directly precedes the function without gaps + if (line == comment$line + 1) { + matched_comment <- comment$text + break + } } + matched_comment + } else { + "" } - matched_comment }, funs$file, funs$line, SIMPLIFY = TRUE) # Generate R functions with or without Roxygen comments @@ -254,9 +258,8 @@ generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) { } else { glue::glue("{name} <- function({list_params}) {{\n {calls}\n}}") } - }, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, SIMPLIFY = FALSE) + }, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, SIMPLIFY = TRUE) - out <- as.character(out) out <- glue::trim(out) out <- glue::glue_collapse(out, sep = "\n\n") unclass(out) From f71771348085ec055d8fc2350e635c57819e9839 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 4 Jan 2025 12:18:39 +0000 Subject: [PATCH 19/46] do not roxygenize chunk in vignette --- vignettes/cpp11.Rmd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vignettes/cpp11.Rmd b/vignettes/cpp11.Rmd index c23a2dbc..741a0ca1 100644 --- a/vignettes/cpp11.Rmd +++ b/vignettes/cpp11.Rmd @@ -354,7 +354,7 @@ This is easy to do in RMarkdown by using `{cpp11}` instead of `{r}` at the begin It is possible to use `roxygen2` to document your C++ functions. Here is an example of how to do this: -```{cpp11} +```{cpp11, eval = FALSE} /* roxygen start @title Mean of a numeric vector @param x A numeric vector @@ -362,7 +362,8 @@ example of how to do this: @examples mean_cpp(1:10) @export roxygen end */ -[[cpp11::register]] double mean_roxygenised_cpp(doubles x) { +[[cpp11::register]] +double mean_roxygenised_cpp(doubles x) { int n = x.size(); double total = 0; for(double value : x) { From fbb365f685d3a44c126740c83021fd16d8787997 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sun, 5 Jan 2025 09:40:42 +0000 Subject: [PATCH 20/46] workaround for roxygen comments in cpp chunks --- vignettes/cpp11.Rmd | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vignettes/cpp11.Rmd b/vignettes/cpp11.Rmd index 741a0ca1..0adcd2d3 100644 --- a/vignettes/cpp11.Rmd +++ b/vignettes/cpp11.Rmd @@ -16,6 +16,14 @@ knitr::opts_chunk$set( comment = "#>", eval = as.logical(Sys.getenv("CPP11_EVAL", "false")) ) +knitr::knit_hooks$set(strip_roxygen = function(before, options, envir) { + if (before) { + code <- options$code + code <- gsub("/\\* roxygen start.*?roxygen end \\*/", "", code, perl = TRUE) + options$code <- code + options + } +}) library(cpp11) ``` @@ -354,7 +362,7 @@ This is easy to do in RMarkdown by using `{cpp11}` instead of `{r}` at the begin It is possible to use `roxygen2` to document your C++ functions. Here is an example of how to do this: -```{cpp11, eval = FALSE} +```{cpp11, strip_roxygen = TRUE} /* roxygen start @title Mean of a numeric vector @param x A numeric vector From c91f4ef648096a7510df1ebe6d189e71c83339ff Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sun, 5 Jan 2025 09:51:04 +0000 Subject: [PATCH 21/46] revert to eval = F --- vignettes/cpp11.Rmd | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/vignettes/cpp11.Rmd b/vignettes/cpp11.Rmd index 0adcd2d3..741a0ca1 100644 --- a/vignettes/cpp11.Rmd +++ b/vignettes/cpp11.Rmd @@ -16,14 +16,6 @@ knitr::opts_chunk$set( comment = "#>", eval = as.logical(Sys.getenv("CPP11_EVAL", "false")) ) -knitr::knit_hooks$set(strip_roxygen = function(before, options, envir) { - if (before) { - code <- options$code - code <- gsub("/\\* roxygen start.*?roxygen end \\*/", "", code, perl = TRUE) - options$code <- code - options - } -}) library(cpp11) ``` @@ -362,7 +354,7 @@ This is easy to do in RMarkdown by using `{cpp11}` instead of `{r}` at the begin It is possible to use `roxygen2` to document your C++ functions. Here is an example of how to do this: -```{cpp11, strip_roxygen = TRUE} +```{cpp11, eval = FALSE} /* roxygen start @title Mean of a numeric vector @param x A numeric vector From b1d8ecdc4a1c2a34f99492cf768bed155ce7f11c Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Sat, 1 Feb 2025 15:36:38 -0500 Subject: [PATCH 22/46] implement #312 --- cpp11test/R/cpp11.R | 8 ++++++++ cpp11test/src/cpp11.cpp | 16 ++++++++++++++++ cpp11test/src/test-external_pointer.cpp | 9 +++++++++ cpp11test/tests/testthat/test-external-pointer.R | 11 +++++++++++ inst/include/cpp11/external_pointer.hpp | 8 +++++--- 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 cpp11test/tests/testthat/test-external-pointer.R diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 038e7b76..8703b3da 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -236,6 +236,14 @@ rcpp_push_and_truncate_ <- function(size_sxp) { .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) } +nullable_extptr_1 <- function() { + .Call(`_cpp11test_nullable_extptr_1`) +} + +nullable_extptr_2 <- function() { + .Call(`_cpp11test_nullable_extptr_2`) +} + test_destruction_inner <- function() { invisible(.Call(`_cpp11test_test_destruction_inner`)) } diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 421de637..781825be 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -443,6 +443,20 @@ extern "C" SEXP _cpp11test_rcpp_push_and_truncate_(SEXP size_sxp) { return cpp11::as_sexp(rcpp_push_and_truncate_(cpp11::as_cpp>(size_sxp))); END_CPP11 } +// test-external_pointer.cpp +cpp11::external_pointer nullable_extptr_1(); +extern "C" SEXP _cpp11test_nullable_extptr_1() { + BEGIN_CPP11 + return cpp11::as_sexp(nullable_extptr_1()); + END_CPP11 +} +// test-external_pointer.cpp +cpp11::external_pointer nullable_extptr_2(); +extern "C" SEXP _cpp11test_nullable_extptr_2() { + BEGIN_CPP11 + return cpp11::as_sexp(nullable_extptr_2()); + END_CPP11 +} // test-protect-nested.cpp void test_destruction_inner(); extern "C" SEXP _cpp11test_test_destruction_inner() { @@ -500,6 +514,8 @@ static const R_CallMethodDef CallEntries[] = { {"_cpp11test_my_warning_n1", (DL_FUNC) &_cpp11test_my_warning_n1, 1}, {"_cpp11test_my_warning_n1fmt", (DL_FUNC) &_cpp11test_my_warning_n1fmt, 1}, {"_cpp11test_my_warning_n2fmt", (DL_FUNC) &_cpp11test_my_warning_n2fmt, 2}, + {"_cpp11test_nullable_extptr_1", (DL_FUNC) &_cpp11test_nullable_extptr_1, 0}, + {"_cpp11test_nullable_extptr_2", (DL_FUNC) &_cpp11test_nullable_extptr_2, 0}, {"_cpp11test_protect_many_", (DL_FUNC) &_cpp11test_protect_many_, 1}, {"_cpp11test_protect_many_cpp11_", (DL_FUNC) &_cpp11test_protect_many_cpp11_, 1}, {"_cpp11test_protect_many_preserve_", (DL_FUNC) &_cpp11test_protect_many_preserve_, 1}, diff --git a/cpp11test/src/test-external_pointer.cpp b/cpp11test/src/test-external_pointer.cpp index 897fd4f2..b8699ae0 100644 --- a/cpp11test/src/test-external_pointer.cpp +++ b/cpp11test/src/test-external_pointer.cpp @@ -10,6 +10,15 @@ void deleter(int* ptr) { delete ptr; } +// Pacha: Test nullable external_pointer (#312) +[[cpp11::register]] cpp11::external_pointer nullable_extptr_1() { + return cpp11::external_pointer(nullptr); +} + +[[cpp11::register]] cpp11::external_pointer nullable_extptr_2() { + return cpp11::external_pointer(R_NilValue); +} + context("external_pointer-C++") { test_that("external_pointer works") { std::vector* v = new std::vector; diff --git a/cpp11test/tests/testthat/test-external-pointer.R b/cpp11test/tests/testthat/test-external-pointer.R new file mode 100644 index 00000000..b82c6d80 --- /dev/null +++ b/cpp11test/tests/testthat/test-external-pointer.R @@ -0,0 +1,11 @@ +# Pacha: test that nullable external pointer is consistent (#312) +test_that("nullable external pointer is consistent", { + + len <- 1e5 + set.seed(42) + x <- rnorm(len) + sum_base <- sum(x) + + expect_equal(nullable_extptr_1(), NULL) + expect_equal(nullable_extptr_2(), NULL) +}) diff --git a/inst/include/cpp11/external_pointer.hpp b/inst/include/cpp11/external_pointer.hpp index a62134ec..fd785c6f 100644 --- a/inst/include/cpp11/external_pointer.hpp +++ b/inst/include/cpp11/external_pointer.hpp @@ -23,8 +23,9 @@ class external_pointer { sexp data_ = R_NilValue; static SEXP valid_type(SEXP data) { - if (data == nullptr) { - throw type_error(EXTPTRSXP, NILSXP); + // Pacha: Allow nullable external_pointer (#312) + if (data == R_NilValue) { + return data; } if (detail::r_typeof(data) != EXTPTRSXP) { throw type_error(EXTPTRSXP, detail::r_typeof(data)); @@ -120,7 +121,8 @@ class external_pointer { data_ = tmp; } - operator bool() noexcept { return data_ != nullptr; } + // Pacha: Support nullable external_pointer (#312) + operator bool() const noexcept { return data_ != R_NilValue; } }; template From 7a72ec90a106ddf72a6a49e253354c7fe77e345f Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 03:31:51 -0400 Subject: [PATCH 23/46] implement fix for #445 --- inst/include/cpp11/list.hpp | 5 ++++- inst/include/cpp11/r_vector.hpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/inst/include/cpp11/list.hpp b/inst/include/cpp11/list.hpp index be29bb2a..66cd99c7 100644 --- a/inst/include/cpp11/list.hpp +++ b/inst/include/cpp11/list.hpp @@ -79,7 +79,8 @@ inline r_vector::r_vector(std::initializer_list il) : cpp11::r_vector(safe[Rf_allocVector](VECSXP, il.size())), capacity_(il.size()) { unwind_protect([&] { - SEXP names = Rf_allocVector(STRSXP, capacity_); + SEXP names; + PROTECT(names = Rf_allocVector(STRSXP, capacity_)); Rf_setAttrib(data_, R_NamesSymbol, names); auto it = il.begin(); @@ -91,6 +92,8 @@ inline r_vector::r_vector(std::initializer_list il) SEXP name = Rf_mkCharCE(it->name(), CE_UTF8); SET_STRING_ELT(names, i, name); } + + UNPROTECT(1); }); } diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 576f4fe6..b2e512fe 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -865,7 +865,8 @@ inline r_vector::r_vector(std::initializer_list il) } unwind_protect([&] { - SEXP names = Rf_allocVector(STRSXP, capacity_); + SEXP names; + PROTECT(names = Rf_allocVector(STRSXP, capacity_)); Rf_setAttrib(data_, R_NamesSymbol, names); auto it = il.begin(); @@ -890,6 +891,8 @@ inline r_vector::r_vector(std::initializer_list il) SEXP name = Rf_mkCharCE(it->name(), CE_UTF8); SET_STRING_ELT(names, i, name); } + + UNPROTECT(1); }); } From fcb7756fe016d953aa5b410aba57678c17ad1615 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 10:32:24 -0400 Subject: [PATCH 24/46] only one cstdlib in data_frame.hpp --- inst/include/cpp11/data_frame.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/inst/include/cpp11/data_frame.hpp b/inst/include/cpp11/data_frame.hpp index a4748de0..6be16400 100644 --- a/inst/include/cpp11/data_frame.hpp +++ b/inst/include/cpp11/data_frame.hpp @@ -1,7 +1,6 @@ #pragma once -#include // for abs -#include +#include // for abs #include // for initializer_list #include // for string, basic_string #include // for move From 012e4e0bd589722fbad43e0cf522bf08166d5251 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 10:33:24 -0400 Subject: [PATCH 25/46] use inline constexpr for doubles/integers/list/logicals/strings --- inst/include/cpp11/doubles.hpp | 2 +- inst/include/cpp11/integers.hpp | 2 +- inst/include/cpp11/list.hpp | 2 +- inst/include/cpp11/logicals.hpp | 2 +- inst/include/cpp11/strings.hpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/inst/include/cpp11/doubles.hpp b/inst/include/cpp11/doubles.hpp index dcab086d..aa5c6044 100644 --- a/inst/include/cpp11/doubles.hpp +++ b/inst/include/cpp11/doubles.hpp @@ -16,7 +16,7 @@ namespace cpp11 { template <> -inline SEXPTYPE r_vector::get_sexptype() { +inline constexpr SEXPTYPE r_vector::get_sexptype() { return REALSXP; } diff --git a/inst/include/cpp11/integers.hpp b/inst/include/cpp11/integers.hpp index 68fd8e4d..dbf8471e 100644 --- a/inst/include/cpp11/integers.hpp +++ b/inst/include/cpp11/integers.hpp @@ -17,7 +17,7 @@ namespace cpp11 { template <> -inline SEXPTYPE r_vector::get_sexptype() { +inline constexpr SEXPTYPE r_vector::get_sexptype() { return INTSXP; } diff --git a/inst/include/cpp11/list.hpp b/inst/include/cpp11/list.hpp index 66cd99c7..e83614f3 100644 --- a/inst/include/cpp11/list.hpp +++ b/inst/include/cpp11/list.hpp @@ -14,7 +14,7 @@ namespace cpp11 { template <> -inline SEXPTYPE r_vector::get_sexptype() { +inline constexpr SEXPTYPE r_vector::get_sexptype() { return VECSXP; } diff --git a/inst/include/cpp11/logicals.hpp b/inst/include/cpp11/logicals.hpp index 45d3d1cc..02bb712c 100644 --- a/inst/include/cpp11/logicals.hpp +++ b/inst/include/cpp11/logicals.hpp @@ -16,7 +16,7 @@ namespace cpp11 { template <> -inline SEXPTYPE r_vector::get_sexptype() { +inline constexpr SEXPTYPE r_vector::get_sexptype() { return LGLSXP; } diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 393f2a8c..aa559be0 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -17,7 +17,7 @@ namespace cpp11 { template <> -inline SEXPTYPE r_vector::get_sexptype() { +inline constexpr SEXPTYPE r_vector::get_sexptype() { return STRSXP; } From 2be654254c63b7689a0464156e976631df004946 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 10:33:42 -0400 Subject: [PATCH 26/46] use noexcept in protect/sexp --- inst/include/cpp11/protect.hpp | 3 ++- inst/include/cpp11/sexp.hpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/inst/include/cpp11/protect.hpp b/inst/include/cpp11/protect.hpp index 9cb4876a..dac0dec5 100644 --- a/inst/include/cpp11/protect.hpp +++ b/inst/include/cpp11/protect.hpp @@ -186,7 +186,8 @@ struct protect { }; constexpr struct protect safe = {}; -inline void check_user_interrupt() { safe[R_CheckUserInterrupt](); } +// @pachadotdev: + noexcept +inline void check_user_interrupt() noexcept { safe[R_CheckUserInterrupt](); } #ifdef CPP11_USE_FMT template diff --git a/inst/include/cpp11/sexp.hpp b/inst/include/cpp11/sexp.hpp index 74205e69..a4a757f3 100644 --- a/inst/include/cpp11/sexp.hpp +++ b/inst/include/cpp11/sexp.hpp @@ -64,8 +64,9 @@ class sexp { return attribute_proxy(*this, R_NamesSymbol); } - operator SEXP() const { return data_; } - SEXP data() const { return data_; } + // @pachadotdev: + noexcept + operator SEXP() const noexcept { return data_; } + SEXP data() const noexcept { return data_; } /// DEPRECATED: Do not use this, it will be removed soon. operator double() const { return REAL_ELT(data_, 0); } From 91c3bd4e6aa4bfa392aefa4992bd34f4817b5e80 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 17:35:20 -0400 Subject: [PATCH 27/46] noexcept in operator SEXP (attribute proxy) --- inst/include/cpp11/attribute_proxy.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/include/cpp11/attribute_proxy.hpp b/inst/include/cpp11/attribute_proxy.hpp index 64e7436b..4b7b4002 100644 --- a/inst/include/cpp11/attribute_proxy.hpp +++ b/inst/include/cpp11/attribute_proxy.hpp @@ -42,7 +42,7 @@ class attribute_proxy { return *this; } - operator SEXP() const { return safe[Rf_getAttrib](parent_.data(), symbol_); } + operator SEXP() const noexcept { return safe[Rf_getAttrib](parent_.data(), symbol_); } }; } // namespace cpp11 From 472f40f96bd014c6008fc5dd32791f797a19555b Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 17:55:17 -0400 Subject: [PATCH 28/46] + noexcept --- inst/include/cpp11/data_frame.hpp | 16 +++++---- inst/include/cpp11/environment.hpp | 5 +-- inst/include/cpp11/external_pointer.hpp | 8 +++-- inst/include/cpp11/function.hpp | 2 +- inst/include/cpp11/matrix.hpp | 5 +-- inst/include/cpp11/named_arg.hpp | 8 +++-- inst/include/cpp11/r_bool.hpp | 3 +- inst/include/cpp11/r_vector.hpp | 44 +++++++++++++++---------- inst/include/cpp11/raws.hpp | 3 +- inst/include/cpp11/sexp.hpp | 7 ++-- inst/include/cpp11/strings.hpp | 20 +++++++---- 11 files changed, 77 insertions(+), 44 deletions(-) diff --git a/inst/include/cpp11/data_frame.hpp b/inst/include/cpp11/data_frame.hpp index 6be16400..41d47150 100644 --- a/inst/include/cpp11/data_frame.hpp +++ b/inst/include/cpp11/data_frame.hpp @@ -35,7 +35,8 @@ class data_frame : public list { return R_NilValue; } - static R_xlen_t calc_nrow(SEXP x) { + // @pachadotdev: + noexcept + static R_xlen_t calc_nrow(SEXP x) noexcept { auto nms = get_attrib0(x, R_RowNamesSymbol); bool has_short_rownames = (Rf_isInteger(nms) && Rf_xlength(nms) == 2 && INTEGER(nms)[0] == NA_INTEGER); @@ -58,8 +59,8 @@ class data_frame : public list { /* Adapted from * https://github.com/wch/r-source/blob/f2a0dfab3e26fb42b8b296fcba40cbdbdbec767d/src/main/attrib.c#L198-L207 */ - R_xlen_t nrow() const { return calc_nrow(*this); } - R_xlen_t ncol() const { return size(); } + R_xlen_t nrow() const noexcept { return calc_nrow(*this); } + R_xlen_t ncol() const noexcept { return size(); } }; namespace writable { @@ -89,13 +90,16 @@ class data_frame : public cpp11::data_frame { using cpp11::data_frame::ncol; using cpp11::data_frame::nrow; - attribute_proxy attr(const char* name) const { return {*this, name}; } + // @pachadotdev: + noexcept + attribute_proxy attr(const char* name) const noexcept const { + return {*this, name}; + } - attribute_proxy attr(const std::string& name) const { + attribute_proxy attr(const std::string& name) const noexcept { return {*this, name.c_str()}; } - attribute_proxy attr(SEXP name) const { return {*this, name}; } + attribute_proxy attr(SEXP name) const noexcept { return {*this, name}; } attribute_proxy names() const { return {*this, R_NamesSymbol}; } }; diff --git a/inst/include/cpp11/environment.hpp b/inst/include/cpp11/environment.hpp index eeee9236..2d9ba0c3 100644 --- a/inst/include/cpp11/environment.hpp +++ b/inst/include/cpp11/environment.hpp @@ -48,9 +48,10 @@ class environment { void remove(const char* name) { remove(safe[Rf_install](name)); } - R_xlen_t size() const { return Rf_xlength(env_); } + // @pachadotdev: + noexcept + R_xlen_t size() const noexcept { return Rf_xlength(env_); } - operator SEXP() const { return env_; } + operator SEXP() const noexcept { return env_; } }; } // namespace cpp11 diff --git a/inst/include/cpp11/external_pointer.hpp b/inst/include/cpp11/external_pointer.hpp index a62134ec..6988c4d6 100644 --- a/inst/include/cpp11/external_pointer.hpp +++ b/inst/include/cpp11/external_pointer.hpp @@ -12,8 +12,9 @@ namespace cpp11 { +// @pachadotdev: + noexcept template -void default_deleter(T* obj) { +void default_deleter(T* obj) noexcept{ delete obj; } @@ -33,12 +34,13 @@ class external_pointer { return data; } - static void r_deleter(SEXP p) { + // @pachadotdev: + noexcept + static void r_deleter(SEXP p) noexcept { if (detail::r_typeof(p) != EXTPTRSXP) return; T* ptr = static_cast(R_ExternalPtrAddr(p)); - if (ptr == NULL) { + if (ptr == nullptr) { return; } diff --git a/inst/include/cpp11/function.hpp b/inst/include/cpp11/function.hpp index 42fbdcf6..448a6b27 100644 --- a/inst/include/cpp11/function.hpp +++ b/inst/include/cpp11/function.hpp @@ -1,6 +1,6 @@ #pragma once -#include // for strcmp +#include // for std::strcmp (@pachadotdev use std qualifiers) #include // for snprintf #include // for string, basic_string diff --git a/inst/include/cpp11/matrix.hpp b/inst/include/cpp11/matrix.hpp index 8345068f..f7990300 100644 --- a/inst/include/cpp11/matrix.hpp +++ b/inst/include/cpp11/matrix.hpp @@ -184,9 +184,10 @@ class matrix : public matrix_slices { SEXP data() const { return vector_.data(); } - R_xlen_t size() const { return vector_.size(); } + // @pachadotdev: + noexcept + R_xlen_t size() const noexcept { return vector_.size(); } - operator SEXP() const { return SEXP(vector_); } + operator SEXP() const noexcept { return SEXP(vector_); } // operator sexp() { return sexp(vector_); } diff --git a/inst/include/cpp11/named_arg.hpp b/inst/include/cpp11/named_arg.hpp index df7ba935..07183bc8 100644 --- a/inst/include/cpp11/named_arg.hpp +++ b/inst/include/cpp11/named_arg.hpp @@ -11,7 +11,8 @@ namespace cpp11 { class named_arg { public: - explicit named_arg(const char* name) : name_(name), value_(R_NilValue) {} + // @pachadotdev: + noexcept + explicit named_arg(const char* name) noexcept : name_(name), value_(R_NilValue) {} named_arg& operator=(std::initializer_list il) { value_ = as_sexp(il); return *this; @@ -39,7 +40,10 @@ class named_arg { namespace literals { -inline named_arg operator""_nm(const char* name, std::size_t) { return named_arg(name); } +// @pachadotdev: + noexcept +inline named_arg operator""_nm(const char* name, std::size_t) noexcept { + return named_arg(name); +} } // namespace literals diff --git a/inst/include/cpp11/r_bool.hpp b/inst/include/cpp11/r_bool.hpp index 4ea36da8..f11b111d 100644 --- a/inst/include/cpp11/r_bool.hpp +++ b/inst/include/cpp11/r_bool.hpp @@ -66,8 +66,9 @@ enable_if_r_bool as_sexp(T from) { return res; } +// @pachadotdev: + noexcept template <> -inline r_bool na() { +inline r_bool na() noexcept { return NA_LOGICAL; } diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index b2e512fe..f1c0691d 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -65,8 +65,9 @@ class r_vector { r_vector& operator=(const r_vector& rhs); r_vector& operator=(r_vector&& rhs); - operator SEXP() const; - operator sexp() const; + // @pachadotdev: + noexcept + operator SEXP() noexcept const; + operator sexp() noexcept const; #ifdef LONG_VECTOR_SUPPORT T operator[](const int pos) const; @@ -85,9 +86,10 @@ class r_vector { bool contains(const r_string& name) const; bool is_altrep() const; bool named() const; - R_xlen_t size() const; - bool empty() const; - SEXP data() const; + // @pachadotdev: + noexcept + R_xlen_t size() noexcept const; + bool empty() no except const; + SEXP data() noexcept const; const sexp attr(const char* name) const; const sexp attr(const std::string& name) const; @@ -95,10 +97,11 @@ class r_vector { r_vector names() const; - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; + // @pachadotdev: + noexcept + const_iterator begin() noexcept const; + const_iterator end() noexcept const; + const_iterator cbegin() noexcept const; + const_iterator cend() noexcept const; const_iterator find(const r_string& name) const; class const_iterator { @@ -448,13 +451,15 @@ inline r_vector& r_vector::operator=(r_vector&& rhs) { return *this; } +// @pachadotdev: + noexcept template -inline r_vector::operator SEXP() const { +inline r_vector::operator SEXP() const noexcept { return data_; } +// @pachadotdev: + noexcept template -inline r_vector::operator sexp() const { +inline r_vector::operator sexp() const noexcept { return data_; } @@ -636,23 +641,27 @@ inline SEXP r_vector::valid_length(SEXP x, R_xlen_t n) { throw std::length_error(message); } +// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::begin() const { +inline typename r_vector::const_iterator r_vector::begin() const noexcept { return const_iterator(this, 0); } +// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::end() const { +inline typename r_vector::const_iterator r_vector::end() const noexcept{ return const_iterator(this, length_); } +// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::cbegin() const { +inline typename r_vector::const_iterator r_vector::cbegin() const noexcept { return const_iterator(this, 0); } +// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::cend() const { +inline typename r_vector::const_iterator r_vector::cend() const noexcept { return const_iterator(this, length_); } @@ -1070,8 +1079,9 @@ inline void r_vector::push_back(T value) { ++length_; } +// @pachadotdev: + noexcept template -inline void r_vector::pop_back() { +inline void r_vector::pop_back() noexcept { --length_; } @@ -1128,7 +1138,7 @@ inline typename r_vector::iterator r_vector::erase(R_xlen_t pos) { } template -inline void r_vector::clear() { +inline void r_vector::clear() noexcept { length_ = 0; } diff --git a/inst/include/cpp11/raws.hpp b/inst/include/cpp11/raws.hpp index f16a6e39..d59d2856 100644 --- a/inst/include/cpp11/raws.hpp +++ b/inst/include/cpp11/raws.hpp @@ -67,9 +67,10 @@ typedef r_vector raws; namespace writable { +// @pachadotdev: + noexcept template <> inline void r_vector::set_elt(SEXP x, R_xlen_t i, - typename r_vector::underlying_type value) { + typename r_vector::underlying_type value) noexcept { // NOPROTECT: Likely too costly to unwind protect every set elt #if R_VERSION >= R_Version(4, 2, 0) SET_RAW_ELT(x, i, value); diff --git a/inst/include/cpp11/sexp.hpp b/inst/include/cpp11/sexp.hpp index a4a757f3..2f1344e1 100644 --- a/inst/include/cpp11/sexp.hpp +++ b/inst/include/cpp11/sexp.hpp @@ -68,12 +68,13 @@ class sexp { operator SEXP() const noexcept { return data_; } SEXP data() const noexcept { return data_; } + // @pachadotdev: + noexcept /// DEPRECATED: Do not use this, it will be removed soon. - operator double() const { return REAL_ELT(data_, 0); } + operator double() const noexcept { return REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. - operator size_t() const { return REAL_ELT(data_, 0); } + operator size_t() const noexcept { returnv REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. - operator bool() const { return LOGICAL_ELT(data_, 0); } + operator bool() const noexcept { return LOGICAL_ELT(data_, 0); } }; } // namespace cpp11 diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index aa559be0..22253066 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -132,17 +132,25 @@ inline r_vector::r_vector(std::initializer_list il) typedef r_vector strings; +// @pachadotdev: +// 1st name - reserve one slot, then emplace +// 2nd, 3rd, ..., Nth name: reserve exactly one more, then emplace template inline void r_vector::push_back(const named_arg& value) { push_back(value.value()); - if (Rf_xlength(names()) == 0) { - cpp11::writable::strings new_nms(size()); - names() = new_nms; - } + cpp11::writable::strings nms(names()); - nms[size() - 1] = value.name(); -} + if (nms.size() == 0) { + nms.reserve(1); + nms.emplace_back(value.name()); + } else { + nms.reserve(nms.size() + 1); + nms.emplace_back(value.name()); + } + + names() = nms; +} } // namespace writable } // namespace cpp11 From 4209cf084e2bb395c1149a7808abe6a3f1d64be2 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 17:55:37 -0400 Subject: [PATCH 29/46] clang format --- inst/include/cpp11/external_pointer.hpp | 2 +- inst/include/cpp11/r_vector.hpp | 2 +- inst/include/cpp11/raws.hpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/inst/include/cpp11/external_pointer.hpp b/inst/include/cpp11/external_pointer.hpp index 6988c4d6..6b63864a 100644 --- a/inst/include/cpp11/external_pointer.hpp +++ b/inst/include/cpp11/external_pointer.hpp @@ -14,7 +14,7 @@ namespace cpp11 { // @pachadotdev: + noexcept template -void default_deleter(T* obj) noexcept{ +void default_deleter(T* obj) noexcept { delete obj; } diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index f1c0691d..8c854116 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -649,7 +649,7 @@ inline typename r_vector::const_iterator r_vector::begin() const noexcept // @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::end() const noexcept{ +inline typename r_vector::const_iterator r_vector::end() const noexcept { return const_iterator(this, length_); } diff --git a/inst/include/cpp11/raws.hpp b/inst/include/cpp11/raws.hpp index d59d2856..9ee2dbc0 100644 --- a/inst/include/cpp11/raws.hpp +++ b/inst/include/cpp11/raws.hpp @@ -69,8 +69,8 @@ namespace writable { // @pachadotdev: + noexcept template <> -inline void r_vector::set_elt(SEXP x, R_xlen_t i, - typename r_vector::underlying_type value) noexcept { +inline void r_vector::set_elt( + SEXP x, R_xlen_t i, typename r_vector::underlying_type value) noexcept { // NOPROTECT: Likely too costly to unwind protect every set elt #if R_VERSION >= R_Version(4, 2, 0) SET_RAW_ELT(x, i, value); From b0b3f472b28b2ed454558a6c2dced8c67f0f8c77 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:11:38 -0400 Subject: [PATCH 30/46] rollback noexcept --- inst/include/cpp11/attribute_proxy.hpp | 2 +- inst/include/cpp11/data_frame.hpp | 14 ++++----- inst/include/cpp11/doubles.hpp | 2 +- inst/include/cpp11/environment.hpp | 5 ++- inst/include/cpp11/external_pointer.hpp | 6 ++-- inst/include/cpp11/integers.hpp | 2 +- inst/include/cpp11/list.hpp | 2 +- inst/include/cpp11/logicals.hpp | 2 +- inst/include/cpp11/matrix.hpp | 5 ++- inst/include/cpp11/named_arg.hpp | 6 ++-- inst/include/cpp11/protect.hpp | 3 +- inst/include/cpp11/r_vector.hpp | 42 ++++++++++--------------- inst/include/cpp11/raws.hpp | 3 +- inst/include/cpp11/sexp.hpp | 12 +++---- 14 files changed, 42 insertions(+), 64 deletions(-) diff --git a/inst/include/cpp11/attribute_proxy.hpp b/inst/include/cpp11/attribute_proxy.hpp index 4b7b4002..64e7436b 100644 --- a/inst/include/cpp11/attribute_proxy.hpp +++ b/inst/include/cpp11/attribute_proxy.hpp @@ -42,7 +42,7 @@ class attribute_proxy { return *this; } - operator SEXP() const noexcept { return safe[Rf_getAttrib](parent_.data(), symbol_); } + operator SEXP() const { return safe[Rf_getAttrib](parent_.data(), symbol_); } }; } // namespace cpp11 diff --git a/inst/include/cpp11/data_frame.hpp b/inst/include/cpp11/data_frame.hpp index 41d47150..15fd9885 100644 --- a/inst/include/cpp11/data_frame.hpp +++ b/inst/include/cpp11/data_frame.hpp @@ -35,8 +35,7 @@ class data_frame : public list { return R_NilValue; } - // @pachadotdev: + noexcept - static R_xlen_t calc_nrow(SEXP x) noexcept { + static R_xlen_t calc_nrow(SEXP x) { auto nms = get_attrib0(x, R_RowNamesSymbol); bool has_short_rownames = (Rf_isInteger(nms) && Rf_xlength(nms) == 2 && INTEGER(nms)[0] == NA_INTEGER); @@ -59,8 +58,8 @@ class data_frame : public list { /* Adapted from * https://github.com/wch/r-source/blob/f2a0dfab3e26fb42b8b296fcba40cbdbdbec767d/src/main/attrib.c#L198-L207 */ - R_xlen_t nrow() const noexcept { return calc_nrow(*this); } - R_xlen_t ncol() const noexcept { return size(); } + R_xlen_t nrow() const { return calc_nrow(*this); } + R_xlen_t ncol() const { return size(); } }; namespace writable { @@ -90,16 +89,15 @@ class data_frame : public cpp11::data_frame { using cpp11::data_frame::ncol; using cpp11::data_frame::nrow; - // @pachadotdev: + noexcept - attribute_proxy attr(const char* name) const noexcept const { + attribute_proxy attr(const char* name) const const { return {*this, name}; } - attribute_proxy attr(const std::string& name) const noexcept { + attribute_proxy attr(const std::string& name) const { return {*this, name.c_str()}; } - attribute_proxy attr(SEXP name) const noexcept { return {*this, name}; } + attribute_proxy attr(SEXP name) const { return {*this, name}; } attribute_proxy names() const { return {*this, R_NamesSymbol}; } }; diff --git a/inst/include/cpp11/doubles.hpp b/inst/include/cpp11/doubles.hpp index aa5c6044..dcab086d 100644 --- a/inst/include/cpp11/doubles.hpp +++ b/inst/include/cpp11/doubles.hpp @@ -16,7 +16,7 @@ namespace cpp11 { template <> -inline constexpr SEXPTYPE r_vector::get_sexptype() { +inline SEXPTYPE r_vector::get_sexptype() { return REALSXP; } diff --git a/inst/include/cpp11/environment.hpp b/inst/include/cpp11/environment.hpp index 2d9ba0c3..eeee9236 100644 --- a/inst/include/cpp11/environment.hpp +++ b/inst/include/cpp11/environment.hpp @@ -48,10 +48,9 @@ class environment { void remove(const char* name) { remove(safe[Rf_install](name)); } - // @pachadotdev: + noexcept - R_xlen_t size() const noexcept { return Rf_xlength(env_); } + R_xlen_t size() const { return Rf_xlength(env_); } - operator SEXP() const noexcept { return env_; } + operator SEXP() const { return env_; } }; } // namespace cpp11 diff --git a/inst/include/cpp11/external_pointer.hpp b/inst/include/cpp11/external_pointer.hpp index 6b63864a..133feab4 100644 --- a/inst/include/cpp11/external_pointer.hpp +++ b/inst/include/cpp11/external_pointer.hpp @@ -12,9 +12,8 @@ namespace cpp11 { -// @pachadotdev: + noexcept template -void default_deleter(T* obj) noexcept { +void default_deleter(T* obj) { delete obj; } @@ -34,8 +33,7 @@ class external_pointer { return data; } - // @pachadotdev: + noexcept - static void r_deleter(SEXP p) noexcept { + static void r_deleter(SEXP p) { if (detail::r_typeof(p) != EXTPTRSXP) return; T* ptr = static_cast(R_ExternalPtrAddr(p)); diff --git a/inst/include/cpp11/integers.hpp b/inst/include/cpp11/integers.hpp index dbf8471e..68fd8e4d 100644 --- a/inst/include/cpp11/integers.hpp +++ b/inst/include/cpp11/integers.hpp @@ -17,7 +17,7 @@ namespace cpp11 { template <> -inline constexpr SEXPTYPE r_vector::get_sexptype() { +inline SEXPTYPE r_vector::get_sexptype() { return INTSXP; } diff --git a/inst/include/cpp11/list.hpp b/inst/include/cpp11/list.hpp index e83614f3..66cd99c7 100644 --- a/inst/include/cpp11/list.hpp +++ b/inst/include/cpp11/list.hpp @@ -14,7 +14,7 @@ namespace cpp11 { template <> -inline constexpr SEXPTYPE r_vector::get_sexptype() { +inline SEXPTYPE r_vector::get_sexptype() { return VECSXP; } diff --git a/inst/include/cpp11/logicals.hpp b/inst/include/cpp11/logicals.hpp index 02bb712c..45d3d1cc 100644 --- a/inst/include/cpp11/logicals.hpp +++ b/inst/include/cpp11/logicals.hpp @@ -16,7 +16,7 @@ namespace cpp11 { template <> -inline constexpr SEXPTYPE r_vector::get_sexptype() { +inline SEXPTYPE r_vector::get_sexptype() { return LGLSXP; } diff --git a/inst/include/cpp11/matrix.hpp b/inst/include/cpp11/matrix.hpp index f7990300..8345068f 100644 --- a/inst/include/cpp11/matrix.hpp +++ b/inst/include/cpp11/matrix.hpp @@ -184,10 +184,9 @@ class matrix : public matrix_slices { SEXP data() const { return vector_.data(); } - // @pachadotdev: + noexcept - R_xlen_t size() const noexcept { return vector_.size(); } + R_xlen_t size() const { return vector_.size(); } - operator SEXP() const noexcept { return SEXP(vector_); } + operator SEXP() const { return SEXP(vector_); } // operator sexp() { return sexp(vector_); } diff --git a/inst/include/cpp11/named_arg.hpp b/inst/include/cpp11/named_arg.hpp index 07183bc8..d27e7e86 100644 --- a/inst/include/cpp11/named_arg.hpp +++ b/inst/include/cpp11/named_arg.hpp @@ -11,8 +11,7 @@ namespace cpp11 { class named_arg { public: - // @pachadotdev: + noexcept - explicit named_arg(const char* name) noexcept : name_(name), value_(R_NilValue) {} + explicit named_arg(const char* name) : name_(name), value_(R_NilValue) {} named_arg& operator=(std::initializer_list il) { value_ = as_sexp(il); return *this; @@ -40,8 +39,7 @@ class named_arg { namespace literals { -// @pachadotdev: + noexcept -inline named_arg operator""_nm(const char* name, std::size_t) noexcept { +inline named_arg operator""_nm(const char* name, std::size_t) { return named_arg(name); } diff --git a/inst/include/cpp11/protect.hpp b/inst/include/cpp11/protect.hpp index dac0dec5..9cb4876a 100644 --- a/inst/include/cpp11/protect.hpp +++ b/inst/include/cpp11/protect.hpp @@ -186,8 +186,7 @@ struct protect { }; constexpr struct protect safe = {}; -// @pachadotdev: + noexcept -inline void check_user_interrupt() noexcept { safe[R_CheckUserInterrupt](); } +inline void check_user_interrupt() { safe[R_CheckUserInterrupt](); } #ifdef CPP11_USE_FMT template diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 8c854116..4973267f 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -65,9 +65,8 @@ class r_vector { r_vector& operator=(const r_vector& rhs); r_vector& operator=(r_vector&& rhs); - // @pachadotdev: + noexcept - operator SEXP() noexcept const; - operator sexp() noexcept const; + operator SEXP() const; + operator sexp() const; #ifdef LONG_VECTOR_SUPPORT T operator[](const int pos) const; @@ -86,10 +85,9 @@ class r_vector { bool contains(const r_string& name) const; bool is_altrep() const; bool named() const; - // @pachadotdev: + noexcept R_xlen_t size() noexcept const; - bool empty() no except const; - SEXP data() noexcept const; + bool empty() const; + SEXP data() const; const sexp attr(const char* name) const; const sexp attr(const std::string& name) const; @@ -97,11 +95,10 @@ class r_vector { r_vector names() const; - // @pachadotdev: + noexcept - const_iterator begin() noexcept const; - const_iterator end() noexcept const; - const_iterator cbegin() noexcept const; - const_iterator cend() noexcept const; + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; const_iterator find(const r_string& name) const; class const_iterator { @@ -451,15 +448,13 @@ inline r_vector& r_vector::operator=(r_vector&& rhs) { return *this; } -// @pachadotdev: + noexcept template -inline r_vector::operator SEXP() const noexcept { +inline r_vector::operator SEXP() const { return data_; } -// @pachadotdev: + noexcept template -inline r_vector::operator sexp() const noexcept { +inline r_vector::operator sexp() const { return data_; } @@ -641,27 +636,23 @@ inline SEXP r_vector::valid_length(SEXP x, R_xlen_t n) { throw std::length_error(message); } -// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::begin() const noexcept { +inline typename r_vector::const_iterator r_vector::begin() const { return const_iterator(this, 0); } -// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::end() const noexcept { +inline typename r_vector::const_iterator r_vector::end() const { return const_iterator(this, length_); } -// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::cbegin() const noexcept { +inline typename r_vector::const_iterator r_vector::cbegin() const { return const_iterator(this, 0); } -// @pachadotdev: + noexcept template -inline typename r_vector::const_iterator r_vector::cend() const noexcept { +inline typename r_vector::const_iterator r_vector::cend() const { return const_iterator(this, length_); } @@ -1079,9 +1070,8 @@ inline void r_vector::push_back(T value) { ++length_; } -// @pachadotdev: + noexcept template -inline void r_vector::pop_back() noexcept { +inline void r_vector::pop_back() { --length_; } @@ -1138,7 +1128,7 @@ inline typename r_vector::iterator r_vector::erase(R_xlen_t pos) { } template -inline void r_vector::clear() noexcept { +inline void r_vector::clear() { length_ = 0; } diff --git a/inst/include/cpp11/raws.hpp b/inst/include/cpp11/raws.hpp index 9ee2dbc0..f0ad9dcd 100644 --- a/inst/include/cpp11/raws.hpp +++ b/inst/include/cpp11/raws.hpp @@ -67,10 +67,9 @@ typedef r_vector raws; namespace writable { -// @pachadotdev: + noexcept template <> inline void r_vector::set_elt( - SEXP x, R_xlen_t i, typename r_vector::underlying_type value) noexcept { + SEXP x, R_xlen_t i, typename r_vector::underlying_type value) { // NOPROTECT: Likely too costly to unwind protect every set elt #if R_VERSION >= R_Version(4, 2, 0) SET_RAW_ELT(x, i, value); diff --git a/inst/include/cpp11/sexp.hpp b/inst/include/cpp11/sexp.hpp index 2f1344e1..11ff8273 100644 --- a/inst/include/cpp11/sexp.hpp +++ b/inst/include/cpp11/sexp.hpp @@ -64,17 +64,15 @@ class sexp { return attribute_proxy(*this, R_NamesSymbol); } - // @pachadotdev: + noexcept - operator SEXP() const noexcept { return data_; } - SEXP data() const noexcept { return data_; } + operator SEXP() const { return data_; } + SEXP data() const { return data_; } - // @pachadotdev: + noexcept /// DEPRECATED: Do not use this, it will be removed soon. - operator double() const noexcept { return REAL_ELT(data_, 0); } + operator double() const { return REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. - operator size_t() const noexcept { returnv REAL_ELT(data_, 0); } + operator size_t() const { returnv REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. - operator bool() const noexcept { return LOGICAL_ELT(data_, 0); } + operator bool() const { return LOGICAL_ELT(data_, 0); } }; } // namespace cpp11 From 8fdaaef06b935e150469f3fdf88fbac0d2dd4f4d Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:12:03 -0400 Subject: [PATCH 31/46] clang format --- inst/include/cpp11/data_frame.hpp | 4 +--- inst/include/cpp11/named_arg.hpp | 4 +--- inst/include/cpp11/raws.hpp | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/inst/include/cpp11/data_frame.hpp b/inst/include/cpp11/data_frame.hpp index 15fd9885..8d337ef3 100644 --- a/inst/include/cpp11/data_frame.hpp +++ b/inst/include/cpp11/data_frame.hpp @@ -89,9 +89,7 @@ class data_frame : public cpp11::data_frame { using cpp11::data_frame::ncol; using cpp11::data_frame::nrow; - attribute_proxy attr(const char* name) const const { - return {*this, name}; - } + attribute_proxy attr(const char* name) const const { return {*this, name}; } attribute_proxy attr(const std::string& name) const { return {*this, name.c_str()}; diff --git a/inst/include/cpp11/named_arg.hpp b/inst/include/cpp11/named_arg.hpp index d27e7e86..df7ba935 100644 --- a/inst/include/cpp11/named_arg.hpp +++ b/inst/include/cpp11/named_arg.hpp @@ -39,9 +39,7 @@ class named_arg { namespace literals { -inline named_arg operator""_nm(const char* name, std::size_t) { - return named_arg(name); -} +inline named_arg operator""_nm(const char* name, std::size_t) { return named_arg(name); } } // namespace literals diff --git a/inst/include/cpp11/raws.hpp b/inst/include/cpp11/raws.hpp index f0ad9dcd..f16a6e39 100644 --- a/inst/include/cpp11/raws.hpp +++ b/inst/include/cpp11/raws.hpp @@ -68,8 +68,8 @@ typedef r_vector raws; namespace writable { template <> -inline void r_vector::set_elt( - SEXP x, R_xlen_t i, typename r_vector::underlying_type value) { +inline void r_vector::set_elt(SEXP x, R_xlen_t i, + typename r_vector::underlying_type value) { // NOPROTECT: Likely too costly to unwind protect every set elt #if R_VERSION >= R_Version(4, 2, 0) SET_RAW_ELT(x, i, value); From a80ae9104375d7edd8be1e80de9c067707a3bf60 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:13:38 -0400 Subject: [PATCH 32/46] fix typos --- inst/include/cpp11/data_frame.hpp | 2 +- inst/include/cpp11/r_bool.hpp | 3 +-- inst/include/cpp11/sexp.hpp | 2 +- inst/include/cpp11/strings.hpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/inst/include/cpp11/data_frame.hpp b/inst/include/cpp11/data_frame.hpp index 8d337ef3..6be16400 100644 --- a/inst/include/cpp11/data_frame.hpp +++ b/inst/include/cpp11/data_frame.hpp @@ -89,7 +89,7 @@ class data_frame : public cpp11::data_frame { using cpp11::data_frame::ncol; using cpp11::data_frame::nrow; - attribute_proxy attr(const char* name) const const { return {*this, name}; } + attribute_proxy attr(const char* name) const { return {*this, name}; } attribute_proxy attr(const std::string& name) const { return {*this, name.c_str()}; diff --git a/inst/include/cpp11/r_bool.hpp b/inst/include/cpp11/r_bool.hpp index f11b111d..4ea36da8 100644 --- a/inst/include/cpp11/r_bool.hpp +++ b/inst/include/cpp11/r_bool.hpp @@ -66,9 +66,8 @@ enable_if_r_bool as_sexp(T from) { return res; } -// @pachadotdev: + noexcept template <> -inline r_bool na() noexcept { +inline r_bool na() { return NA_LOGICAL; } diff --git a/inst/include/cpp11/sexp.hpp b/inst/include/cpp11/sexp.hpp index 11ff8273..74205e69 100644 --- a/inst/include/cpp11/sexp.hpp +++ b/inst/include/cpp11/sexp.hpp @@ -70,7 +70,7 @@ class sexp { /// DEPRECATED: Do not use this, it will be removed soon. operator double() const { return REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. - operator size_t() const { returnv REAL_ELT(data_, 0); } + operator size_t() const { return REAL_ELT(data_, 0); } /// DEPRECATED: Do not use this, it will be removed soon. operator bool() const { return LOGICAL_ELT(data_, 0); } }; diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 22253066..5bf76e4e 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -17,7 +17,7 @@ namespace cpp11 { template <> -inline constexpr SEXPTYPE r_vector::get_sexptype() { +inline SEXPTYPE r_vector::get_sexptype() { return STRSXP; } From ff8e314b14df75fe49877f470981ff5aee7a5ef7 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:14:34 -0400 Subject: [PATCH 33/46] fix r_Vector --- inst/include/cpp11/r_vector.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 4973267f..b2e512fe 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -85,7 +85,7 @@ class r_vector { bool contains(const r_string& name) const; bool is_altrep() const; bool named() const; - R_xlen_t size() noexcept const; + R_xlen_t size() const; bool empty() const; SEXP data() const; From f7fd0e05d8a0f46e3c5c9d6c13b9a7b3fcf09047 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:23:42 -0400 Subject: [PATCH 34/46] fix strings --- inst/include/cpp11/strings.hpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 5bf76e4e..61ccf15b 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -137,17 +137,10 @@ typedef r_vector strings; // 2nd, 3rd, ..., Nth name: reserve exactly one more, then emplace template inline void r_vector::push_back(const named_arg& value) { - push_back(value.value()); - cpp11::writable::strings nms(names()); - if (nms.size() == 0) { - nms.reserve(1); - nms.emplace_back(value.name()); - } else { - nms.reserve(nms.size() + 1); - nms.emplace_back(value.name()); - } + nms.reserve(nms.size() + 1); + nms.push_back(value.name()); names() = nms; } From e0229d54faccbffe557581fc0d234251fb6998a9 Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:31:11 -0400 Subject: [PATCH 35/46] rollback strings --- inst/include/cpp11/strings.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 61ccf15b..8b788464 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -137,12 +137,13 @@ typedef r_vector strings; // 2nd, 3rd, ..., Nth name: reserve exactly one more, then emplace template inline void r_vector::push_back(const named_arg& value) { + push_back(value.value()); + if (Rf_xlength(names()) == 0) { + cpp11::writable::strings new_nms(size()); + names() = new_nms; + } cpp11::writable::strings nms(names()); - - nms.reserve(nms.size() + 1); - nms.push_back(value.name()); - - names() = nms; + nms[size() - 1] = value.name(); } } // namespace writable From ddf53006b13101e8101beafef04f51aca2e4e3ea Mon Sep 17 00:00:00 2001 From: Mauricio 'Pacha' Vargas Sepulveda Date: Fri, 9 May 2025 19:31:56 -0400 Subject: [PATCH 36/46] rollback strings --- inst/include/cpp11/strings.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 8b788464..393f2a8c 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -132,9 +132,6 @@ inline r_vector::r_vector(std::initializer_list il) typedef r_vector strings; -// @pachadotdev: -// 1st name - reserve one slot, then emplace -// 2nd, 3rd, ..., Nth name: reserve exactly one more, then emplace template inline void r_vector::push_back(const named_arg& value) { push_back(value.value()); @@ -145,6 +142,7 @@ inline void r_vector::push_back(const named_arg& value) { cpp11::writable::strings nms(names()); nms[size() - 1] = value.name(); } + } // namespace writable } // namespace cpp11 From 98514233cd327fa98eed274c5369903664599875 Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Tue, 26 Aug 2025 15:45:03 -0400 Subject: [PATCH 37/46] use a lamba to unwind protect around the loop --- inst/include/cpp11/as.hpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 682f12b5..4243d1de 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -287,18 +287,14 @@ template SEXP as_sexp_strings(const Container& from, AsCstring&& c_str) { R_xlen_t size = from.size(); - SEXP data; - try { - data = PROTECT(safe[Rf_allocVector](STRSXP, size)); + SEXP data = PROTECT(safe[Rf_allocVector](STRSXP, size)); + unwind_protect([&] { auto it = from.begin(); for (R_xlen_t i = 0; i < size; ++i, ++it) { - SET_STRING_ELT(data, i, safe[Rf_mkCharCE](c_str(*it), CE_UTF8)); + SET_STRING_ELT(data, i, Rf_mkCharCE(c_str(*it), CE_UTF8)); } - } catch (const unwind_exception& e) { - UNPROTECT(1); - throw e; - } + }); UNPROTECT(1); return data; From c016918cd3bc4816de507bcb59f8383821f3acab Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Tue, 26 Aug 2025 16:10:58 -0400 Subject: [PATCH 38/46] Use values added to a vector with push_back immediately --- cpp11test/src/test-integers.cpp | 68 +++++++++++++++++++++++++++++++++ inst/include/cpp11/integers.hpp | 23 +++++++++++ inst/include/cpp11/r_vector.hpp | 27 +++++++++++++ 3 files changed, 118 insertions(+) diff --git a/cpp11test/src/test-integers.cpp b/cpp11test/src/test-integers.cpp index bfc581bf..53bdf905 100644 --- a/cpp11test/src/test-integers.cpp +++ b/cpp11test/src/test-integers.cpp @@ -138,6 +138,22 @@ context("integers-C++") { expect_true(x[1] == 3); expect_true(x[2] == 4); } + test_that("integers.value()") { + cpp11::writable::integers x; + x.push_back(1); + x.push_back(2); + x.push_back(3); + + // Test that value() returns the same as operator[] but as T directly + expect_true(x.value(0) == 1); + expect_true(x.value(1) == 2); + expect_true(x.value(2) == 3); + + // Test that value() works with C-style formatting (this was the original issue in #453) + expect_true(x.value(0) == x[0]); + expect_true(x.value(1) == x[1]); + expect_true(x.value(2) == x[2]); + } test_that("writable::integers(SEXP)") { SEXP x = PROTECT(Rf_allocVector(INTSXP, 5)); @@ -278,4 +294,56 @@ context("integers-C++") { int y = NA_INTEGER; expect_true(cpp11::is_na(y)); } + + test_that("proxy issue demonstration") { + // This test demonstrates the proxy issue and shows that all solutions work + cpp11::writable::integers x; + for (int i = 0; i < 3; i++) { + x.push_back(i * 10); + } + + // Test that value() method works correctly + expect_true(x.value(0) == 0); + expect_true(x.value(1) == 10); + expect_true(x.value(2) == 20); + + // Test that explicit cast works + expect_true((int)x[0] == 0); + expect_true((int)x[1] == 10); + expect_true((int)x[2] == 20); + + // Test that auto assignment works (triggers implicit conversion) + int val0 = x[0]; + int val1 = x[1]; + int val2 = x[2]; + expect_true(val0 == 0); + expect_true(val1 == 10); + expect_true(val2 == 20); + + // Test that value() and operator[] return equivalent results + expect_true(x.value(0) == (int)x[0]); + expect_true(x.value(1) == (int)x[1]); + expect_true(x.value(2) == (int)x[2]); + } +} + +// [[cpp11::register]] +// Demo function to show the three ways to handle the proxy issue +// To use this function: +// 1. Run cpp11::cpp_register() to regenerate R bindings +// 2. Rebuild and reinstall the package +// 3. Call test_proxy_issue_demo() from R +void test_proxy_issue_demo() { + cpp11::writable::integers x; + for (int i = 0; i < 5; i++) { + x.push_back(i); + + // These all work correctly: + Rprintf("Method 1 - cast: x[%d] = %d\n", i, (int)x[i]); + Rprintf("Method 2 - value(): x[%d] = %d\n", i, x.value(i)); + + // This also works (auto triggers implicit conversion): + int val = x[i]; + Rprintf("Method 3 - auto: x[%d] = %d\n", i, val); + } } diff --git a/inst/include/cpp11/integers.hpp b/inst/include/cpp11/integers.hpp index 68fd8e4d..be20886e 100644 --- a/inst/include/cpp11/integers.hpp +++ b/inst/include/cpp11/integers.hpp @@ -102,3 +102,26 @@ inline integers as_integers(SEXP x) { } } // namespace cpp11 + +// Note: Proxy Behavior in writable::integers +// +// When using writable::integers, operator[] returns a proxy object that allows +// both reading and writing. For cases where you need the actual int value +// (e.g., when using with C-style variadic functions like Rprintf), use one of +// these three approaches: +// +// 1. Direct value access: vec.value(i) [Recommended] +// 2. Explicit cast: (int)vec[i] +// 3. Auto with explicit type: int val = vec[i]; +// +// Example demonstrating the issue and solutions: +// writable::integers vec; +// vec.push_back(42); +// +// // This may print garbage due to proxy object: +// // Rprintf("Value: %d\n", vec[0]); // DON'T DO THIS +// +// // These all work correctly: +// Rprintf("Value: %d\n", vec.value(0)); // Recommended +// Rprintf("Value: %d\n", (int)vec[0]); // Also works +// int val = vec[0]; Rprintf("Value: %d\n", val); // Also works diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 576f4fe6..c3fc3006 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -256,6 +256,15 @@ class r_vector : public cpp11::r_vector { iterator find(const r_string& name) const; + /// Get the value at position without returning a proxy + /// This is useful when you need the actual value (e.g., for C-style printf functions) + /// that don't trigger implicit conversions from proxy types +#ifdef LONG_VECTOR_SUPPORT + T value(const int pos) const; +#endif + T value(const R_xlen_t pos) const; + T value(const size_type pos) const; + attribute_proxy> attr(const char* name) const; attribute_proxy> attr(const std::string& name) const; attribute_proxy> attr(SEXP name) const; @@ -1156,6 +1165,24 @@ inline typename r_vector::iterator r_vector::find(const r_string& name) co return end(); } +#ifdef LONG_VECTOR_SUPPORT +template +inline T r_vector::value(const int pos) const { + return value(static_cast(pos)); +} +#endif + +template +inline T r_vector::value(const R_xlen_t pos) const { + // Use the parent read-only class's operator[] which returns T directly + return cpp11::r_vector::operator[](pos); +} + +template +inline T r_vector::value(const size_type pos) const { + return value(static_cast(pos)); +} + template inline attribute_proxy> r_vector::attr(const char* name) const { return attribute_proxy>(*this, name); From 8ee112a353fb1402b834c074670bf737c8a31fb5 Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Tue, 26 Aug 2025 16:12:29 -0400 Subject: [PATCH 39/46] fix clang format --- cpp11test/src/test-integers.cpp | 19 ++++++++++--------- inst/include/cpp11/integers.hpp | 14 +++++++------- inst/include/cpp11/list_of.hpp | 6 ++++-- tests/testthat/single_error.cpp | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cpp11test/src/test-integers.cpp b/cpp11test/src/test-integers.cpp index 53bdf905..aaaa69f2 100644 --- a/cpp11test/src/test-integers.cpp +++ b/cpp11test/src/test-integers.cpp @@ -148,8 +148,9 @@ context("integers-C++") { expect_true(x.value(0) == 1); expect_true(x.value(1) == 2); expect_true(x.value(2) == 3); - - // Test that value() works with C-style formatting (this was the original issue in #453) + + // Test that value() works with C-style formatting (this was the original issue in + // #453) expect_true(x.value(0) == x[0]); expect_true(x.value(1) == x[1]); expect_true(x.value(2) == x[2]); @@ -306,20 +307,20 @@ context("integers-C++") { expect_true(x.value(0) == 0); expect_true(x.value(1) == 10); expect_true(x.value(2) == 20); - + // Test that explicit cast works expect_true((int)x[0] == 0); expect_true((int)x[1] == 10); expect_true((int)x[2] == 20); - + // Test that auto assignment works (triggers implicit conversion) int val0 = x[0]; - int val1 = x[1]; + int val1 = x[1]; int val2 = x[2]; expect_true(val0 == 0); expect_true(val1 == 10); expect_true(val2 == 20); - + // Test that value() and operator[] return equivalent results expect_true(x.value(0) == (int)x[0]); expect_true(x.value(1) == (int)x[1]); @@ -331,17 +332,17 @@ context("integers-C++") { // Demo function to show the three ways to handle the proxy issue // To use this function: // 1. Run cpp11::cpp_register() to regenerate R bindings -// 2. Rebuild and reinstall the package +// 2. Rebuild and reinstall the package // 3. Call test_proxy_issue_demo() from R void test_proxy_issue_demo() { cpp11::writable::integers x; for (int i = 0; i < 5; i++) { x.push_back(i); - + // These all work correctly: Rprintf("Method 1 - cast: x[%d] = %d\n", i, (int)x[i]); Rprintf("Method 2 - value(): x[%d] = %d\n", i, x.value(i)); - + // This also works (auto triggers implicit conversion): int val = x[i]; Rprintf("Method 3 - auto: x[%d] = %d\n", i, val); diff --git a/inst/include/cpp11/integers.hpp b/inst/include/cpp11/integers.hpp index be20886e..9784b0ff 100644 --- a/inst/include/cpp11/integers.hpp +++ b/inst/include/cpp11/integers.hpp @@ -105,23 +105,23 @@ inline integers as_integers(SEXP x) { // Note: Proxy Behavior in writable::integers // -// When using writable::integers, operator[] returns a proxy object that allows -// both reading and writing. For cases where you need the actual int value -// (e.g., when using with C-style variadic functions like Rprintf), use one of +// When using writable::integers, operator[] returns a proxy object that allows +// both reading and writing. For cases where you need the actual int value +// (e.g., when using with C-style variadic functions like Rprintf), use one of // these three approaches: // // 1. Direct value access: vec.value(i) [Recommended] -// 2. Explicit cast: (int)vec[i] +// 2. Explicit cast: (int)vec[i] // 3. Auto with explicit type: int val = vec[i]; // // Example demonstrating the issue and solutions: // writable::integers vec; // vec.push_back(42); -// +// // // This may print garbage due to proxy object: // // Rprintf("Value: %d\n", vec[0]); // DON'T DO THIS -// +// // // These all work correctly: // Rprintf("Value: %d\n", vec.value(0)); // Recommended -// Rprintf("Value: %d\n", (int)vec[0]); // Also works +// Rprintf("Value: %d\n", (int)vec[0]); // Also works // int val = vec[0]; Rprintf("Value: %d\n", val); // Also works diff --git a/inst/include/cpp11/list_of.hpp b/inst/include/cpp11/list_of.hpp index 86c39acb..c8ed1fa0 100644 --- a/inst/include/cpp11/list_of.hpp +++ b/inst/include/cpp11/list_of.hpp @@ -54,13 +54,15 @@ class list_of : public writable::list { #ifdef LONG_VECTOR_SUPPORT proxy operator[](int pos) { - return {writable::list::operator[](static_cast(pos))}; + return { writable::list::operator[](static_cast(pos)) }; } #endif proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } - proxy operator[](const char* pos) { return {writable::list::operator[](pos)}; } + proxy operator[](const char* pos) { + return { writable::list::operator[](pos) }; + } proxy operator[](const std::string& pos) { return writable::list::operator[](pos.c_str()); diff --git a/tests/testthat/single_error.cpp b/tests/testthat/single_error.cpp index 8f9b8963..5d30c7af 100644 --- a/tests/testthat/single_error.cpp +++ b/tests/testthat/single_error.cpp @@ -1 +1 @@ -[[cpp11::register]] int foo() { return 1 } +[[cpp11::register]] int foo(){return 1} From 8093e127f34346cdf735ee44a04fe256537ff92a Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Sat, 6 Sep 2025 22:49:51 -0400 Subject: [PATCH 40/46] llvm formatt --- inst/include/cpp11/list_of.hpp | 56 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/inst/include/cpp11/list_of.hpp b/inst/include/cpp11/list_of.hpp index c8ed1fa0..7edcc0b9 100644 --- a/inst/include/cpp11/list_of.hpp +++ b/inst/include/cpp11/list_of.hpp @@ -1,51 +1,57 @@ #pragma once -#include // for string, basic_string +#include // for string, basic_string -#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT -#include "cpp11/list.hpp" // for list +#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT +#include "cpp11/list.hpp" // for list namespace cpp11 { -template -class list_of : public list { - public: - list_of(const list& data) : list(data) {} +template class list_of : public list { +public: + list_of(const list &data) : list(data) {} #ifdef LONG_VECTOR_SUPPORT - T operator[](const int pos) const { return operator[](static_cast(pos)); } + T operator[](const int pos) const { + return operator[](static_cast(pos)); + } #endif T operator[](const R_xlen_t pos) const { return list::operator[](pos); } - T operator[](const char* pos) const { return list::operator[](pos); } + T operator[](const char *pos) const { return list::operator[](pos); } - T operator[](const std::string& pos) const { return list::operator[](pos.c_str()); } + T operator[](const std::string &pos) const { + return list::operator[](pos.c_str()); + } }; namespace writable { -template -class list_of : public writable::list { - public: - list_of(const list& data) : writable::list(data) {} +template class list_of : public writable::list { +public: + list_of(const list &data) : writable::list(data) {} list_of(R_xlen_t n) : writable::list(n) {} class proxy { - private: + private: writable::list::proxy data_; - public: - proxy(const writable::list::proxy& data) : data_(data) {} + public: + proxy(const writable::list::proxy &data) : data_(data) {} operator T() const { return static_cast(*this); } operator SEXP() const { return static_cast(data_); } #ifdef LONG_VECTOR_SUPPORT typename T::proxy operator[](int pos) { return static_cast(data_)[pos]; } #endif - typename T::proxy operator[](R_xlen_t pos) { return static_cast(data_)[pos]; } - proxy operator[](const char* pos) { static_cast(data_)[pos]; } - proxy operator[](const std::string& pos) { return static_cast(data_)[pos]; } - proxy& operator=(const T& rhs) { + typename T::proxy operator[](R_xlen_t pos) { + return static_cast(data_)[pos]; + } + proxy operator[](const char *pos) { static_cast(data_)[pos]; } + proxy operator[](const std::string &pos) { + return static_cast(data_)[pos]; + } + proxy &operator=(const T &rhs) { data_ = rhs; return *this; @@ -60,14 +66,14 @@ class list_of : public writable::list { proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } - proxy operator[](const char* pos) { + proxy operator[](const char *pos) { return { writable::list::operator[](pos) }; } - proxy operator[](const std::string& pos) { + proxy operator[](const std::string &pos) { return writable::list::operator[](pos.c_str()); } }; -} // namespace writable +} // namespace writable -} // namespace cpp11 +} // namespace cpp11 From 6783944077a7289fddd3141e25074a7c5181c2ff Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Mon, 15 Sep 2025 09:48:51 -0400 Subject: [PATCH 41/46] Global symbol visibility --- R/source.R | 21 +++++--- cpp11test/src/test-integers.cpp | 69 -------------------------- man/cpp_source.Rd | 19 +++++-- tests/testthat/test-source-local.R | 80 ++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 78 deletions(-) create mode 100644 tests/testthat/test-source-local.R diff --git a/R/source.R b/R/source.R index fc9124d5..3aa172ea 100644 --- a/R/source.R +++ b/R/source.R @@ -18,6 +18,13 @@ #' uses 'CXX11' if unset. #' @param dir The directory to store the generated source files. `tempfile()` is #' used by default. The directory will be removed if `clean` is `TRUE`. +#' @param local Passed to [dyn.load()]. If `TRUE` (the default) the shared +#' library is loaded with local symbols; if `FALSE` symbols are made global +#' (equivalent to `dyn.load(..., local = FALSE)`), which can be required when +#' other shared objects need to see RTTI/vtable symbols from this library. +#' @note See the unit test that demonstrates this usage at +#' \code{tests/testthat/test-source-local.R} (shows how `local = FALSE` exports +#' the necessary symbols so separate shared objects can link against them). #' @return For [cpp_source()] and `[cpp_function()]` the results of #' [dyn.load()] (invisibly). For `[cpp_eval()]` the results of the evaluated #' expression. @@ -65,7 +72,7 @@ #' } #' #' @export -cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), dir = tempfile()) { +cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), dir = tempfile(), local = TRUE) { stop_unless_installed(c("brio", "callr", "cli", "decor", "desc", "glue", "tibble", "vctrs")) if (!missing(file) && !file.exists(file)) { stop("Can't find `file` at this path:\n", file, "\n", call. = FALSE) @@ -145,7 +152,7 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu brio::write_lines(r_functions, r_path) source(r_path, local = env) - dyn.load(shared_lib, local = TRUE, now = TRUE) + dyn.load(shared_lib, local = local, now = TRUE) } the <- new.env(parent = emptyenv()) @@ -183,7 +190,7 @@ generate_makevars <- function(includes, cxx_std) { #' @rdname cpp_source #' @export -cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11")) { +cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), local = TRUE) { cpp_source(code = paste(c('#include "cpp11.hpp"', "using namespace ::cpp11;", "namespace writable = ::cpp11::writable;", @@ -193,7 +200,8 @@ cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, env = env, clean = clean, quiet = quiet, - cxx_std = cxx_std + cxx_std = cxx_std, + local = local ) } @@ -201,7 +209,7 @@ utils::globalVariables("f") #' @rdname cpp_source #' @export -cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11")) { +cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), local = TRUE) { cpp_source(code = paste(c('#include "cpp11.hpp"', "using namespace ::cpp11;", "namespace writable = ::cpp11::writable;", @@ -214,7 +222,8 @@ cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx env = env, clean = clean, quiet = quiet, - cxx_std = cxx_std + cxx_std = cxx_std, + local = local ) f() } diff --git a/cpp11test/src/test-integers.cpp b/cpp11test/src/test-integers.cpp index aaaa69f2..bfc581bf 100644 --- a/cpp11test/src/test-integers.cpp +++ b/cpp11test/src/test-integers.cpp @@ -138,23 +138,6 @@ context("integers-C++") { expect_true(x[1] == 3); expect_true(x[2] == 4); } - test_that("integers.value()") { - cpp11::writable::integers x; - x.push_back(1); - x.push_back(2); - x.push_back(3); - - // Test that value() returns the same as operator[] but as T directly - expect_true(x.value(0) == 1); - expect_true(x.value(1) == 2); - expect_true(x.value(2) == 3); - - // Test that value() works with C-style formatting (this was the original issue in - // #453) - expect_true(x.value(0) == x[0]); - expect_true(x.value(1) == x[1]); - expect_true(x.value(2) == x[2]); - } test_that("writable::integers(SEXP)") { SEXP x = PROTECT(Rf_allocVector(INTSXP, 5)); @@ -295,56 +278,4 @@ context("integers-C++") { int y = NA_INTEGER; expect_true(cpp11::is_na(y)); } - - test_that("proxy issue demonstration") { - // This test demonstrates the proxy issue and shows that all solutions work - cpp11::writable::integers x; - for (int i = 0; i < 3; i++) { - x.push_back(i * 10); - } - - // Test that value() method works correctly - expect_true(x.value(0) == 0); - expect_true(x.value(1) == 10); - expect_true(x.value(2) == 20); - - // Test that explicit cast works - expect_true((int)x[0] == 0); - expect_true((int)x[1] == 10); - expect_true((int)x[2] == 20); - - // Test that auto assignment works (triggers implicit conversion) - int val0 = x[0]; - int val1 = x[1]; - int val2 = x[2]; - expect_true(val0 == 0); - expect_true(val1 == 10); - expect_true(val2 == 20); - - // Test that value() and operator[] return equivalent results - expect_true(x.value(0) == (int)x[0]); - expect_true(x.value(1) == (int)x[1]); - expect_true(x.value(2) == (int)x[2]); - } -} - -// [[cpp11::register]] -// Demo function to show the three ways to handle the proxy issue -// To use this function: -// 1. Run cpp11::cpp_register() to regenerate R bindings -// 2. Rebuild and reinstall the package -// 3. Call test_proxy_issue_demo() from R -void test_proxy_issue_demo() { - cpp11::writable::integers x; - for (int i = 0; i < 5; i++) { - x.push_back(i); - - // These all work correctly: - Rprintf("Method 1 - cast: x[%d] = %d\n", i, (int)x[i]); - Rprintf("Method 2 - value(): x[%d] = %d\n", i, x.value(i)); - - // This also works (auto triggers implicit conversion): - int val = x[i]; - Rprintf("Method 3 - auto: x[%d] = %d\n", i, val); - } } diff --git a/man/cpp_source.Rd b/man/cpp_source.Rd index 87164ef5..0fdaa3e1 100644 --- a/man/cpp_source.Rd +++ b/man/cpp_source.Rd @@ -13,7 +13,8 @@ cpp_source( clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), - dir = tempfile() + dir = tempfile(), + local = TRUE ) cpp_function( @@ -21,7 +22,8 @@ cpp_function( env = parent.frame(), clean = TRUE, quiet = TRUE, - cxx_std = Sys.getenv("CXX_STD", "CXX11") + cxx_std = Sys.getenv("CXX_STD", "CXX11"), + local = TRUE ) cpp_eval( @@ -29,7 +31,8 @@ cpp_eval( env = parent.frame(), clean = TRUE, quiet = TRUE, - cxx_std = Sys.getenv("CXX_STD", "CXX11") + cxx_std = Sys.getenv("CXX_STD", "CXX11"), + local = TRUE ) } \arguments{ @@ -49,6 +52,11 @@ uses 'CXX11' if unset.} \item{dir}{The directory to store the generated source files. \code{tempfile()} is used by default. The directory will be removed if \code{clean} is \code{TRUE}.} + +\item{local}{Passed to \code{\link[=dyn.load]{dyn.load()}}. If \code{TRUE} (the default) the shared +library is loaded with local symbols; if \code{FALSE} symbols are made global +(equivalent to \code{dyn.load(..., local = FALSE)}), which can be required when +other shared objects need to see RTTI/vtable symbols from this library.} } \value{ For \code{\link[=cpp_source]{cpp_source()}} and \verb{[cpp_function()]} the results of @@ -65,6 +73,11 @@ Within C++ code you can use \verb{[[cpp11::linking_to("pkgxyz")]]} to link to external packages. This is equivalent to putting those packages in the \code{LinkingTo} field in a package DESCRIPTION. } +\note{ +See the unit test that demonstrates this usage at +\code{tests/testthat/test-source-local.R} (shows how \code{local = FALSE} exports +the necessary symbols so separate shared objects can link against them). +} \examples{ cpp_source( diff --git a/tests/testthat/test-source-local.R b/tests/testthat/test-source-local.R new file mode 100644 index 00000000..328d735f --- /dev/null +++ b/tests/testthat/test-source-local.R @@ -0,0 +1,80 @@ +test_that("cpp_source local controls RTTI/vtable symbol visibility", { + skip_on_os("windows") + + mk_dirs <- function() { + d1 <- tempfile("cpp_source_local1") + d2 <- tempfile("cpp_source_local2") + dir.create(d1); dir.create(d2) + list(provider = d1, consumer = d2) + } + + unload_dirs <- function(dirs) { + dlls <- getLoadedDLLs() + for (nm in names(dlls)) { + p <- dlls[[nm]]$path + if (!is.null(p)) { + for (d in dirs) { + if (grepl(d, p, fixed = TRUE)) { + tryCatch(dyn.unload(p), error = function(e) NULL) + } + } + } + } + } + + provider_so_path <- function(dir) { + src <- file.path(dir, "src") + files <- list.files(src, pattern = paste0("\\\\", .Platform$dynlib.ext, "$"), ignore.case = TRUE) + if (length(files) == 0) return(character()) + file.path(src, files[[1]]) + } + + dirs <- mk_dirs() + on.exit({ + unload_dirs(unlist(dirs)) + unlink(unlist(dirs), recursive = TRUE, force = TRUE) + }, add = TRUE) + + # Provider: abstract Base + Impl factory (polymorphic triggers RTTI/vtable) + provider_code <- ' + #include + struct Base { virtual ~Base(){}; virtual int foo() = 0; }; + struct Impl : Base { int foo() override { return 77; } }; + + extern "C" Base* make_impl() { return new Impl(); } + extern "C" void destroy_impl(Base* p) { delete p; } + ' + + # Consumer uses typeid(Base) (forces reference to typeinfo symbol) and + # calls the factory produced by the provider. + consumer_code <- '\n#include \n#include \n#include \nstruct Base { virtual ~Base(){}; virtual int foo() = 0; };\nextern "C" Base* make_impl();\nextern "C" SEXP call_typeinfo_and_run() {\n const std::type_info& t = typeid(Base);\n std::string n = t.name();\n Base* b = make_impl();\n int v = b->foo();\n delete b;\n SEXP out = PROTECT(Rf_allocVector(INTSXP, 2));\n INTEGER(out)[0] = (int)n.size();\n INTEGER(out)[1] = v;\n UNPROTECT(1);\n return out;\n}\n' + + # 1) provider loaded with local = TRUE -> consumer should fail to load + expect_silent(cpp_source(code = provider_code, dir = dirs$provider, clean = FALSE, local = TRUE)) + expect_error( + cpp_source(code = consumer_code, dir = dirs$consumer, clean = FALSE), + regexp = "undefined symbol|symbol .* not found|undefined reference", + ignore.case = TRUE + ) + + # Clean up partial loads + unload_dirs(unlist(dirs)) + + # 2) provider loaded with local = FALSE -> consumer loads and runs + expect_silent(cpp_source(code = provider_code, dir = dirs$provider, clean = FALSE, local = FALSE)) + expect_silent(cpp_source(code = consumer_code, dir = dirs$consumer, clean = FALSE)) + + res <- .Call("call_typeinfo_and_run") + expect_true(is.integer(res) && length(res) == 2) + expect_equal(as.integer(res)[2], 77L) + expect_true(as.integer(res)[1] > 0) + + # Explicit check that the manual dyn.load(...) workaround is unnecessary. + # Emulate the snippet to locate the provider shared object and show that it + # exists; we already demonstrated the consumer works without running this + # manual snippet because cpp_source(local = FALSE) provided global symbols. + so_path <- provider_so_path(dirs$provider) + expect_true(file.exists(so_path)) + # Loading it manually with local = FALSE would succeed, but wasn't required. + expect_silent(dyn.load(so_path, local = FALSE, now = TRUE)) +}) From 511441128b3f85752477874a3aa89198aa37a20c Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Mon, 15 Sep 2025 09:50:32 -0400 Subject: [PATCH 42/46] clang ofrmat --- inst/include/cpp11/list_of.hpp | 56 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/inst/include/cpp11/list_of.hpp b/inst/include/cpp11/list_of.hpp index 7edcc0b9..c8ed1fa0 100644 --- a/inst/include/cpp11/list_of.hpp +++ b/inst/include/cpp11/list_of.hpp @@ -1,57 +1,51 @@ #pragma once -#include // for string, basic_string +#include // for string, basic_string -#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT -#include "cpp11/list.hpp" // for list +#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT +#include "cpp11/list.hpp" // for list namespace cpp11 { -template class list_of : public list { -public: - list_of(const list &data) : list(data) {} +template +class list_of : public list { + public: + list_of(const list& data) : list(data) {} #ifdef LONG_VECTOR_SUPPORT - T operator[](const int pos) const { - return operator[](static_cast(pos)); - } + T operator[](const int pos) const { return operator[](static_cast(pos)); } #endif T operator[](const R_xlen_t pos) const { return list::operator[](pos); } - T operator[](const char *pos) const { return list::operator[](pos); } + T operator[](const char* pos) const { return list::operator[](pos); } - T operator[](const std::string &pos) const { - return list::operator[](pos.c_str()); - } + T operator[](const std::string& pos) const { return list::operator[](pos.c_str()); } }; namespace writable { -template class list_of : public writable::list { -public: - list_of(const list &data) : writable::list(data) {} +template +class list_of : public writable::list { + public: + list_of(const list& data) : writable::list(data) {} list_of(R_xlen_t n) : writable::list(n) {} class proxy { - private: + private: writable::list::proxy data_; - public: - proxy(const writable::list::proxy &data) : data_(data) {} + public: + proxy(const writable::list::proxy& data) : data_(data) {} operator T() const { return static_cast(*this); } operator SEXP() const { return static_cast(data_); } #ifdef LONG_VECTOR_SUPPORT typename T::proxy operator[](int pos) { return static_cast(data_)[pos]; } #endif - typename T::proxy operator[](R_xlen_t pos) { - return static_cast(data_)[pos]; - } - proxy operator[](const char *pos) { static_cast(data_)[pos]; } - proxy operator[](const std::string &pos) { - return static_cast(data_)[pos]; - } - proxy &operator=(const T &rhs) { + typename T::proxy operator[](R_xlen_t pos) { return static_cast(data_)[pos]; } + proxy operator[](const char* pos) { static_cast(data_)[pos]; } + proxy operator[](const std::string& pos) { return static_cast(data_)[pos]; } + proxy& operator=(const T& rhs) { data_ = rhs; return *this; @@ -66,14 +60,14 @@ template class list_of : public writable::list { proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } - proxy operator[](const char *pos) { + proxy operator[](const char* pos) { return { writable::list::operator[](pos) }; } - proxy operator[](const std::string &pos) { + proxy operator[](const std::string& pos) { return writable::list::operator[](pos.c_str()); } }; -} // namespace writable +} // namespace writable -} // namespace cpp11 +} // namespace cpp11 From acdcf618a727d8653a98bf554ef11a5b74356969 Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Mon, 15 Sep 2025 09:51:36 -0400 Subject: [PATCH 43/46] clang format --- inst/include/cpp11/list_of.hpp | 56 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/inst/include/cpp11/list_of.hpp b/inst/include/cpp11/list_of.hpp index 7edcc0b9..c8ed1fa0 100644 --- a/inst/include/cpp11/list_of.hpp +++ b/inst/include/cpp11/list_of.hpp @@ -1,57 +1,51 @@ #pragma once -#include // for string, basic_string +#include // for string, basic_string -#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT -#include "cpp11/list.hpp" // for list +#include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT +#include "cpp11/list.hpp" // for list namespace cpp11 { -template class list_of : public list { -public: - list_of(const list &data) : list(data) {} +template +class list_of : public list { + public: + list_of(const list& data) : list(data) {} #ifdef LONG_VECTOR_SUPPORT - T operator[](const int pos) const { - return operator[](static_cast(pos)); - } + T operator[](const int pos) const { return operator[](static_cast(pos)); } #endif T operator[](const R_xlen_t pos) const { return list::operator[](pos); } - T operator[](const char *pos) const { return list::operator[](pos); } + T operator[](const char* pos) const { return list::operator[](pos); } - T operator[](const std::string &pos) const { - return list::operator[](pos.c_str()); - } + T operator[](const std::string& pos) const { return list::operator[](pos.c_str()); } }; namespace writable { -template class list_of : public writable::list { -public: - list_of(const list &data) : writable::list(data) {} +template +class list_of : public writable::list { + public: + list_of(const list& data) : writable::list(data) {} list_of(R_xlen_t n) : writable::list(n) {} class proxy { - private: + private: writable::list::proxy data_; - public: - proxy(const writable::list::proxy &data) : data_(data) {} + public: + proxy(const writable::list::proxy& data) : data_(data) {} operator T() const { return static_cast(*this); } operator SEXP() const { return static_cast(data_); } #ifdef LONG_VECTOR_SUPPORT typename T::proxy operator[](int pos) { return static_cast(data_)[pos]; } #endif - typename T::proxy operator[](R_xlen_t pos) { - return static_cast(data_)[pos]; - } - proxy operator[](const char *pos) { static_cast(data_)[pos]; } - proxy operator[](const std::string &pos) { - return static_cast(data_)[pos]; - } - proxy &operator=(const T &rhs) { + typename T::proxy operator[](R_xlen_t pos) { return static_cast(data_)[pos]; } + proxy operator[](const char* pos) { static_cast(data_)[pos]; } + proxy operator[](const std::string& pos) { return static_cast(data_)[pos]; } + proxy& operator=(const T& rhs) { data_ = rhs; return *this; @@ -66,14 +60,14 @@ template class list_of : public writable::list { proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } - proxy operator[](const char *pos) { + proxy operator[](const char* pos) { return { writable::list::operator[](pos) }; } - proxy operator[](const std::string &pos) { + proxy operator[](const std::string& pos) { return writable::list::operator[](pos.c_str()); } }; -} // namespace writable +} // namespace writable -} // namespace cpp11 +} // namespace cpp11 From e340f78e0b8a4e0ce2eaddaa9a29ef5a95ec59af Mon Sep 17 00:00:00 2001 From: Mauricio Vargas Sepulveda Date: Mon, 15 Sep 2025 10:40:48 -0400 Subject: [PATCH 44/46] clang format 12 --- README.md | 10 + inst/include/cpp11/list_of.hpp | 6 +- inst/include/fmt/core.h | 1262 +++++++++++++++---------------- inst/include/fmt/format-inl.h | 485 ++++++------ inst/include/fmt/format.h | 1000 ++++++++++++------------ tests/testthat/single_error.cpp | 2 +- 6 files changed, 1319 insertions(+), 1446 deletions(-) diff --git a/README.md b/README.md index d6f37dae..b5947fd4 100644 --- a/README.md +++ b/README.md @@ -78,3 +78,13 @@ Please note that the cpp11 project is released with a [Contributor Code of Condu cpp11 would not exist without Rcpp. Thanks to the Rcpp authors, Dirk Eddelbuettel, Romain Francois, JJ Allaire, Kevin Ushey, Qiang Kou, Nathan Russell, Douglas Bates and John Chambers for their work writing and maintaining Rcpp. + +## Clang format + +To match GHA, use clang-format-12 to format C++ code. With systems that provide clang-format-14 or newer, you can use Docker: + +```bash +docker run --rm -v "$PWD":/work -w /work ubuntu:22.04 bash -lc "\ + apt-get update && apt-get install -y clang-format-12 && \ + find . -name '*.cpp' -o -name '*.hpp' -o -name '*.h' | xargs -r clang-format-12 -i" +``` diff --git a/inst/include/cpp11/list_of.hpp b/inst/include/cpp11/list_of.hpp index c8ed1fa0..86c39acb 100644 --- a/inst/include/cpp11/list_of.hpp +++ b/inst/include/cpp11/list_of.hpp @@ -54,15 +54,13 @@ class list_of : public writable::list { #ifdef LONG_VECTOR_SUPPORT proxy operator[](int pos) { - return { writable::list::operator[](static_cast(pos)) }; + return {writable::list::operator[](static_cast(pos))}; } #endif proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } - proxy operator[](const char* pos) { - return { writable::list::operator[](pos) }; - } + proxy operator[](const char* pos) { return {writable::list::operator[](pos)}; } proxy operator[](const std::string& pos) { return writable::list::operator[](pos.c_str()); diff --git a/inst/include/fmt/core.h b/inst/include/fmt/core.h index 4c1f5e2c..26084206 100644 --- a/inst/include/fmt/core.h +++ b/inst/include/fmt/core.h @@ -19,62 +19,62 @@ #define FMT_VERSION 80000 #ifdef __clang__ -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else -# define FMT_CLANG_VERSION 0 +#define FMT_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#define FMT_GCC_PRAGMA(arg) _Pragma(arg) #else -# define FMT_GCC_VERSION 0 -# define FMT_GCC_PRAGMA(arg) +#define FMT_GCC_VERSION 0 +#define FMT_GCC_PRAGMA(arg) #endif #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) -# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION #else -# define FMT_HAS_GXX_CXX11 0 +#define FMT_HAS_GXX_CXX11 0 #endif #if defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER +#define FMT_ICC_VERSION __INTEL_COMPILER #else -# define FMT_ICC_VERSION 0 +#define FMT_ICC_VERSION 0 #endif #ifdef __NVCC__ -# define FMT_NVCC __NVCC__ +#define FMT_NVCC __NVCC__ #else -# define FMT_NVCC 0 +#define FMT_NVCC 0 #endif #ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#define FMT_MSC_VER _MSC_VER +#define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_MSC_VER 0 -# define FMT_MSC_WARNING(...) +#define FMT_MSC_VER 0 +#define FMT_MSC_WARNING(...) #endif #ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) +#define FMT_HAS_FEATURE(x) __has_feature(x) #else -# define FMT_HAS_FEATURE(x) 0 +#define FMT_HAS_FEATURE(x) 0 #endif #if defined(__has_include) && !defined(__INTELLISENSE__) && \ (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) -# define FMT_HAS_INCLUDE(x) __has_include(x) +#define FMT_HAS_INCLUDE(x) __has_include(x) #else -# define FMT_HAS_INCLUDE(x) 0 +#define FMT_HAS_INCLUDE(x) 0 #endif #ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ @@ -86,224 +86,218 @@ // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +#define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC && !FMT_ICC_VERSION #endif #if FMT_USE_CONSTEXPR -# define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr +#define FMT_CONSTEXPR constexpr +#define FMT_CONSTEXPR_DECL constexpr #else -# define FMT_CONSTEXPR -# define FMT_CONSTEXPR_DECL +#define FMT_CONSTEXPR +#define FMT_CONSTEXPR_DECL #endif // Check if constexpr std::char_traits<>::compare,length is supported. #if defined(__GLIBCXX__) -# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ - _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. +#define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#endif +#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && _LIBCPP_VERSION >= 4000 +#define FMT_CONSTEXPR_CHAR_TRAITS constexpr #elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#define FMT_CONSTEXPR_CHAR_TRAITS constexpr #endif #ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS +#define FMT_CONSTEXPR_CHAR_TRAITS #endif #ifndef FMT_OVERRIDE -# if FMT_HAS_FEATURE(cxx_override_control) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_OVERRIDE override -# else -# define FMT_OVERRIDE -# endif +#if FMT_HAS_FEATURE(cxx_override_control) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +#define FMT_OVERRIDE override +#else +#define FMT_OVERRIDE +#endif #endif // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS -# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -# else -# define FMT_EXCEPTIONS 1 -# endif +#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || FMT_MSC_VER && !_HAS_EXCEPTIONS +#define FMT_EXCEPTIONS 0 +#else +#define FMT_EXCEPTIONS 1 +#endif #endif // Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). #ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 +#define FMT_USE_NOEXCEPT 0 #endif #if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept -# define FMT_HAS_CXX11_NOEXCEPT 1 +#define FMT_DETECTED_NOEXCEPT noexcept +#define FMT_HAS_CXX11_NOEXCEPT 1 #else -# define FMT_DETECTED_NOEXCEPT throw() -# define FMT_HAS_CXX11_NOEXCEPT 0 +#define FMT_DETECTED_NOEXCEPT throw() +#define FMT_HAS_CXX11_NOEXCEPT 0 #endif #ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT -# else -# define FMT_NOEXCEPT -# endif +#if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +#define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +#define FMT_NOEXCEPT +#endif #endif // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code // warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ - !FMT_NVCC -# define FMT_NORETURN [[noreturn]] +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && !FMT_NVCC +#define FMT_NORETURN [[noreturn]] #else -# define FMT_NORETURN +#define FMT_NORETURN #endif #ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif +#if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +#define FMT_MAYBE_UNUSED [[maybe_unused]] +#else +#define FMT_MAYBE_UNUSED +#endif #endif #if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_FALLTHROUGH -# elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -# elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] -# else -# define FMT_FALLTHROUGH -# endif +#if defined(__INTEL_COMPILER) || defined(__PGI) +#define FMT_FALLTHROUGH +#elif defined(__clang__) +#define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +#define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +#define FMT_FALLTHROUGH +#endif #elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define FMT_FALLTHROUGH [[fallthrough]] +#define FMT_FALLTHROUGH [[fallthrough]] #else -# define FMT_FALLTHROUGH +#define FMT_FALLTHROUGH #endif #ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 +#define FMT_USE_FLOAT 1 #endif #ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 +#define FMT_USE_DOUBLE 1 #endif #ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 +#define FMT_USE_LONG_DOUBLE 1 #endif #ifndef FMT_INLINE -# if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_INLINE inline __attribute__((always_inline)) -# else -# define FMT_INLINE inline -# endif +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +#define FMT_INLINE inline __attribute__((always_inline)) +#else +#define FMT_INLINE inline +#endif #endif #ifndef FMT_USE_INLINE_NAMESPACES -# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ - (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED)) -# define FMT_USE_INLINE_NAMESPACES 1 -# else -# define FMT_USE_INLINE_NAMESPACES 0 -# endif +#if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED)) +#define FMT_USE_INLINE_NAMESPACES 1 +#else +#define FMT_USE_INLINE_NAMESPACES 0 +#endif #endif #ifndef FMT_BEGIN_NAMESPACE -# if FMT_USE_INLINE_NAMESPACES -# define FMT_INLINE_NAMESPACE inline namespace -# define FMT_END_NAMESPACE \ - } \ - } -# else -# define FMT_INLINE_NAMESPACE namespace -# define FMT_END_NAMESPACE \ - } \ - using namespace v7; \ - } -# endif -# define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ - FMT_INLINE_NAMESPACE v7 { +#if FMT_USE_INLINE_NAMESPACES +#define FMT_INLINE_NAMESPACE inline namespace +#define FMT_END_NAMESPACE \ + } \ + } +#else +#define FMT_INLINE_NAMESPACE namespace +#define FMT_END_NAMESPACE \ + } \ + using namespace v7; \ + } +#endif +#define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v7 { #endif #ifndef FMT_MODULE_EXPORT -# define FMT_MODULE_EXPORT -# define FMT_MODULE_EXPORT_BEGIN -# define FMT_MODULE_EXPORT_END -# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { -# define FMT_END_DETAIL_NAMESPACE } +#define FMT_MODULE_EXPORT +#define FMT_MODULE_EXPORT_BEGIN +#define FMT_MODULE_EXPORT_END +#define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +#define FMT_END_DETAIL_NAMESPACE } #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) -# ifdef FMT_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif +#define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) +#ifdef FMT_EXPORT +#define FMT_API __declspec(dllexport) +#elif defined(FMT_SHARED) +#define FMT_API __declspec(dllimport) +#endif #else -# define FMT_CLASS_API -# if defined(FMT_EXPORT) || defined(FMT_SHARED) -# if defined(__GNUC__) || defined(__clang__) -# define FMT_API __attribute__((visibility("default"))) -# endif -# endif +#define FMT_CLASS_API +#if defined(FMT_EXPORT) || defined(FMT_SHARED) +#if defined(__GNUC__) || defined(__clang__) +#define FMT_API __attribute__((visibility("default"))) +#endif +#endif #endif #ifndef FMT_API -# define FMT_API +#define FMT_API #endif #if FMT_GCC_VERSION -# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) #else -# define FMT_GCC_VISIBILITY_HIDDEN +#define FMT_GCC_VISIBILITY_HIDDEN #endif // libc++ supports string_view in pre-c++17. #if (FMT_HAS_INCLUDE() && \ (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) -# include -# define FMT_USE_STRING_VIEW +#include +#define FMT_USE_STRING_VIEW #elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#include +#define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +#define FMT_UNICODE !FMT_MSC_VER #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L) || \ - (defined(__cpp_consteval) && \ - !FMT_MSC_VER) // consteval is broken in MSVC. -# define FMT_CONSTEVAL consteval -# define FMT_HAS_CONSTEVAL -# else -# define FMT_CONSTEVAL -# endif +#if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && __cplusplus > 201703L) || \ + (defined(__cpp_consteval) && !FMT_MSC_VER) // consteval is broken in MSVC. +#define FMT_CONSTEVAL consteval +#define FMT_HAS_CONSTEVAL +#else +#define FMT_CONSTEVAL +#endif #endif #ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 -# else -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 -# endif +#if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) +#define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +#else +#define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +#endif #endif // Enable minimal optimizations for more compact code in debug mode. @@ -320,13 +314,18 @@ template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; -template using bool_constant = std::integral_constant; +template +using bool_constant = std::integral_constant; template using remove_reference_t = typename std::remove_reference::type; template using remove_cvref_t = typename std::remove_cv>::type; -template struct type_identity { using type = T; }; -template using type_identity_t = typename type_identity::type; +template +struct type_identity { + using type = T; +}; +template +using type_identity_t = typename type_identity::type; struct monostate { constexpr monostate() {} @@ -336,9 +335,9 @@ struct monostate { // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // to workaround a bug in MSVC 2019 (see #1140 and #1186). #ifdef FMT_DOC -# define FMT_ENABLE_IF(...) +#define FMT_ENABLE_IF(...) #else -# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 #endif FMT_BEGIN_DETAIL_NAMESPACE @@ -352,58 +351,62 @@ constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool { } // A function to suppress "conditional expression is constant" warnings. -template constexpr auto const_check(T value) -> T { return value; } +template +constexpr auto const_check(T value) -> T { + return value; +} -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - const char* message); +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, const char* message); #ifndef FMT_ASSERT -# ifdef NDEBUG +#ifdef NDEBUG // FMT_ASSERT is not empty to avoid -Werror=empty-body. -# define FMT_ASSERT(condition, message) ((void)0) -# else -# define FMT_ASSERT(condition, message) \ - ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ - ? (void)0 \ - : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) -# endif +#define FMT_ASSERT(condition, message) ((void)0) +#else +#define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +#endif #endif #if defined(FMT_USE_STRING_VIEW) -template using std_string_view = std::basic_string_view; +template +using std_string_view = std::basic_string_view; #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) template using std_string_view = std::experimental::basic_string_view; #else -template struct std_string_view {}; +template +struct std_string_view {}; #endif #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) -# define FMT_USE_INT128 1 +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER) +#define FMT_USE_INT128 1 using int128_t = __int128_t; using uint128_t = __uint128_t; -template inline auto convert_for_visit(T value) -> T { +template +inline auto convert_for_visit(T value) -> T { return value; } #else -# define FMT_USE_INT128 0 +#define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 enum class int128_t {}; enum class uint128_t {}; // Reduce template instantiations. -template inline auto convert_for_visit(T) -> monostate { +template +inline auto convert_for_visit(T) -> monostate { return {}; } #endif // Casts a nonnegative integer to unsigned. template -FMT_CONSTEXPR auto to_unsigned(Int value) -> - typename std::make_unsigned::type { +FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type { FMT_ASSERT(value >= 0, "negative value"); return static_cast::type>(value); } @@ -414,8 +417,8 @@ constexpr auto is_utf8() -> bool { // Avoid buggy sign extensions in MSVC's constant evaluation mode. // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 using uchar = unsigned char; - return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && - uchar(micro[1]) == 0xB5); + return FMT_UNICODE || + (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && uchar(micro[1]) == 0xB5); } FMT_END_DETAIL_NAMESPACE @@ -426,7 +429,8 @@ FMT_END_DETAIL_NAMESPACE compiled with a different ``-std`` option than the client code (which is not recommended). */ -template class basic_string_view { +template +class basic_string_view { private: const Char* data_; size_t size_; @@ -438,9 +442,8 @@ template class basic_string_view { constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT - : data_(s), - size_(count) {} + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT : data_(s), + size_(count) {} /** \rst @@ -460,15 +463,13 @@ template class basic_string_view { /** Constructs a string reference from a ``std::basic_string`` object. */ template - FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) FMT_NOEXCEPT - : data_(s.data()), - size_(s.size()) {} + FMT_CONSTEXPR basic_string_view(const std::basic_string& s) + FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} - template >::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), size_(s.size()) {} /** Returns a pointer to the string data. */ constexpr auto data() const -> const Char* { return data_; } @@ -479,9 +480,7 @@ template class basic_string_view { constexpr auto begin() const -> iterator { return data_; } constexpr auto end() const -> iterator { return data_ + size_; } - constexpr auto operator[](size_t pos) const -> const Char& { - return data_[pos]; - } + constexpr auto operator[](size_t pos) const -> const Char& { return data_[pos]; } FMT_CONSTEXPR void remove_prefix(size_t n) { data_ += n; @@ -492,14 +491,12 @@ template class basic_string_view { FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { size_t str_size = size_ < other.size_ ? size_ : other.size_; int result = std::char_traits::compare(data_, other.data_, str_size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + if (result == 0) result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); return result; } FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, - basic_string_view rhs) - -> bool { + basic_string_view rhs) -> bool { return lhs.compare(rhs) == 0; } friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { @@ -522,8 +519,10 @@ template class basic_string_view { using string_view = basic_string_view; /** Specifies if ``T`` is a character type. Can be specialized by users. */ -template struct is_char : std::false_type {}; -template <> struct is_char : std::true_type {}; +template +struct is_char : std::false_type {}; +template <> +struct is_char : std::true_type {}; /** \rst @@ -553,15 +552,13 @@ inline auto to_string_view(const std::basic_string& s) } template -constexpr auto to_string_view(basic_string_view s) - -> basic_string_view { +constexpr auto to_string_view(basic_string_view s) -> basic_string_view { return s; } template >::value)> -inline auto to_string_view(detail::std_string_view s) - -> basic_string_view { +inline auto to_string_view(detail::std_string_view s) -> basic_string_view { return s; } @@ -573,8 +570,7 @@ template struct is_compile_string : std::is_base_of {}; template ::value)> -constexpr auto to_string_view(const S& s) - -> basic_string_view { +constexpr auto to_string_view(const S& s) -> basic_string_view { return basic_string_view(s); } @@ -587,11 +583,12 @@ using fmt::v7::to_string_view; // It should be a constexpr function but MSVC 2017 fails to compile it in // enable_if and MSVC 2015 fails to compile it as an alias template. template -struct is_string : std::is_class()))> { -}; +struct is_string : std::is_class()))> {}; -template struct char_t_impl {}; -template struct char_t_impl::value>> { +template +struct char_t_impl {}; +template +struct char_t_impl::value>> { using result = decltype(to_string_view(std::declval())); using type = typename result::value_type; }; @@ -618,7 +615,8 @@ struct error_handler { FMT_END_DETAIL_NAMESPACE /** String's character type. */ -template using char_t = typename detail::char_t_impl::type; +template +using char_t = typename detail::char_t_impl::type; /** \rst @@ -637,25 +635,20 @@ class basic_format_parse_context : private ErrorHandler { using char_type = Char; using iterator = typename basic_string_view::iterator; - explicit constexpr basic_format_parse_context( - basic_string_view format_str, ErrorHandler eh = {}, - int next_arg_id = 0) + explicit constexpr basic_format_parse_context(basic_string_view format_str, + ErrorHandler eh = {}, int next_arg_id = 0) : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} /** Returns an iterator to the beginning of the format string range being parsed. */ - constexpr auto begin() const FMT_NOEXCEPT -> iterator { - return format_str_.begin(); - } + constexpr auto begin() const FMT_NOEXCEPT -> iterator { return format_str_.begin(); } /** Returns an iterator past the end of the format string range being parsed. */ - constexpr auto end() const FMT_NOEXCEPT -> iterator { - return format_str_.end(); - } + constexpr auto end() const FMT_NOEXCEPT -> iterator { return format_str_.end(); } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -687,18 +680,19 @@ class basic_format_parse_context : private ErrorHandler { FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - FMT_CONSTEXPR void on_error(const char* message) { - ErrorHandler::on_error(message); - } + FMT_CONSTEXPR void on_error(const char* message) { ErrorHandler::on_error(message); } constexpr auto error_handler() const -> ErrorHandler { return *this; } }; using format_parse_context = basic_format_parse_context; -template class basic_format_arg; -template class basic_format_args; -template class dynamic_format_arg_store; +template +class basic_format_arg; +template +class basic_format_args; +template +class dynamic_format_arg_store; // A formatter for objects of type T. template @@ -710,11 +704,11 @@ struct formatter { // Specifies if T has an enabled formatter specialization. A type can be // formattable even if it doesn't have a formatter e.g. via a conversion. template -using has_formatter = - std::is_constructible>; +using has_formatter = std::is_constructible>; // Checks whether T is a container with contiguous storage. -template struct is_contiguous : std::false_type {}; +template +struct is_contiguous : std::false_type {}; template struct is_contiguous> : std::true_type {}; @@ -724,8 +718,7 @@ FMT_BEGIN_DETAIL_NAMESPACE // Extracts a reference to the container from back_insert_iterator. template -inline auto get_container(std::back_insert_iterator it) - -> Container& { +inline auto get_container(std::back_insert_iterator it) -> Container& { using bi_iterator = std::back_insert_iterator; struct accessor : bi_iterator { accessor(bi_iterator iter) : bi_iterator(iter) {} @@ -735,17 +728,14 @@ inline auto get_container(std::back_insert_iterator it) } template -FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) - -> OutputIt { +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) -> OutputIt { while (begin != end) *out++ = static_cast(*begin++); return out; } template ::value)> -FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) - -> Char* { - if (is_constant_evaluated()) - return copy_str(begin, end, out); +FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) -> Char* { + if (is_constant_evaluated()) return copy_str(begin, end, out); auto size = to_unsigned(end - begin); memcpy(out, begin, size); return out + size; @@ -757,7 +747,8 @@ FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. \endrst */ -template class buffer { +template +class buffer { private: T* ptr_; size_t size_; @@ -768,10 +759,9 @@ template class buffer { FMT_MSC_WARNING(suppress : 26495) buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} - buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT - : ptr_(p), - size_(sz), - capacity_(cap) {} + buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT : ptr_(p), + size_(sz), + capacity_(cap) {} ~buffer() = default; buffer(buffer&&) = default; @@ -834,10 +824,15 @@ template class buffer { } /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); + template + void append(const U* begin, const U* end); - template auto operator[](I index) -> T& { return ptr_[index]; } - template auto operator[](I index) const -> const T& { + template + auto operator[](I index) -> T& { + return ptr_[index]; + } + template + auto operator[](I index) const -> const T& { return ptr_[index]; } }; @@ -896,7 +891,8 @@ class iterator_buffer final : public Traits, public buffer { auto count() const -> size_t { return Traits::count() + this->size(); } }; -template class iterator_buffer final : public buffer { +template +class iterator_buffer final : public buffer { protected: void grow(size_t) final FMT_OVERRIDE {} @@ -908,9 +904,9 @@ template class iterator_buffer final : public buffer { // A buffer that writes to a container with the contiguous storage. template -class iterator_buffer, - enable_if_t::value, - typename Container::value_type>> +class iterator_buffer< + std::back_insert_iterator, + enable_if_t::value, typename Container::value_type>> final : public buffer { private: Container& container_; @@ -932,7 +928,8 @@ class iterator_buffer, }; // A buffer that counts the number of code units written discarding the output. -template class counting_buffer final : public buffer { +template +class counting_buffer final : public buffer { private: enum { buffer_size = 256 }; T data_[buffer_size]; @@ -965,7 +962,8 @@ template auto get_iterator(Buffer& buf) -> decltype(buf.out()) { return buf.out(); } -template auto get_iterator(buffer& buf) -> buffer_appender { +template +auto get_iterator(buffer& buf) -> buffer_appender { return buffer_appender(buf); } @@ -976,18 +974,19 @@ struct fallback_formatter { // Specifies if T has an enabled fallback_formatter specialization. template -using has_fallback_formatter = - std::is_constructible>; +using has_fallback_formatter = std::is_constructible>; struct view {}; -template struct named_arg : view { +template +struct named_arg : view { const Char* name; const T& value; named_arg(const Char* n, const T& v) : name(n), value(v) {} }; -template struct named_arg_info { +template +struct named_arg_info { const Char* name; int id; }; @@ -1014,45 +1013,49 @@ struct arg_data { template FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } - FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { - return nullptr; - } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { return nullptr; } }; template inline void init_named_args(named_arg_info*, int, int) {} -template struct is_named_arg : std::false_type {}; -template struct is_statically_named_arg : std::false_type {}; +template +struct is_named_arg : std::false_type {}; +template +struct is_statically_named_arg : std::false_type {}; template struct is_named_arg> : std::true_type {}; template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T&, const Tail&... args) { +void init_named_args(named_arg_info* named_args, int arg_count, int named_arg_count, + const T&, const Tail&... args) { init_named_args(named_args, arg_count + 1, named_arg_count, args...); } template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T& arg, const Tail&... args) { +void init_named_args(named_arg_info* named_args, int arg_count, int named_arg_count, + const T& arg, const Tail&... args) { named_args[named_arg_count++] = {arg.name, arg_count}; init_named_args(named_args, arg_count + 1, named_arg_count, args...); } template -FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, - const Args&...) {} +FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} -template constexpr auto count() -> size_t { return B ? 1 : 0; } -template constexpr auto count() -> size_t { +template +constexpr auto count() -> size_t { + return B ? 1 : 0; +} +template +constexpr auto count() -> size_t { return (B1 ? 1 : 0) + count(); } -template constexpr auto count_named_args() -> size_t { +template +constexpr auto count_named_args() -> size_t { return count::value...>(); } @@ -1085,8 +1088,7 @@ struct type_constant : std::integral_constant {}; #define FMT_TYPE_CONSTANT(Type, constant) \ template \ - struct type_constant \ - : std::integral_constant {} + struct type_constant : std::integral_constant {} FMT_TYPE_CONSTANT(int, int_type); FMT_TYPE_CONSTANT(unsigned, uint_type); @@ -1111,24 +1113,28 @@ constexpr bool is_arithmetic_type(type t) { return t > type::none_type && t <= type::last_numeric_type; } -template struct string_value { +template +struct string_value { const Char* data; size_t size; }; -template struct named_arg_value { +template +struct named_arg_value { const named_arg_info* data; size_t size; }; -template struct custom_value { +template +struct custom_value { using parse_context = typename Context::parse_context_type; const void* value; void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); }; // A formatting argument value. -template class value { +template +class value { public: using char_type = typename Context::char_type; @@ -1175,15 +1181,16 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_CONSTEXPR FMT_INLINE value(const T& val) { + template + FMT_CONSTEXPR FMT_INLINE value(const T& val) { custom.value = &val; // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. - custom.format = format_custom_arg< - T, conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>>; + custom.format = + format_custom_arg::value, + typename Context::template formatter_type, + fallback_formatter>>; } private: @@ -1210,26 +1217,20 @@ using ulong_type = conditional_t; struct unformattable {}; // Maps formatting arguments to core types. -template struct arg_mapper { +template +struct arg_mapper { using char_type = typename Context::char_type; FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { return val; } FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { return val; } FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { return val; } FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) - -> unsigned long long { + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) -> unsigned long long { return val; } FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } @@ -1238,49 +1239,39 @@ template struct arg_mapper { template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { - static_assert( - std::is_same::value || std::is_same::value, - "mixing character types is disallowed"); + static_assert(std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); return val; } FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { return val; } FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { return val; } template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { static_assert(std::is_same>::value, "mixing character types is disallowed"); return to_string_view(val); } template , T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { + FMT_ENABLE_IF(std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return basic_string_view(val); } template < typename T, - FMT_ENABLE_IF( - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { + FMT_ENABLE_IF(std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return std_string_view(val); } FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -> const char* { @@ -1301,12 +1292,8 @@ template struct arg_mapper { } FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { return val; } // We use SFINAE instead of a const T* parameter to avoid conflicting with // the C array overload. @@ -1326,18 +1313,16 @@ template struct arg_mapper { } template ::value && - !has_formatter::value && + FMT_ENABLE_IF(std::is_enum::value && !has_formatter::value && !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( static_cast::type>(val))) { return map(static_cast::type>(val)); } - template ::value && !is_char::value && - (has_formatter::value || - has_fallback_formatter::value))> + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& { return val; } @@ -1394,7 +1379,8 @@ class appender : public std::back_insert_iterator> { // A formatting argument. It is a trivially copyable/constructible type to // allow storage in basic_memory_buffer. -template class basic_format_arg { +template +class basic_format_arg { private: detail::value value_; detail::type type_; @@ -1424,8 +1410,7 @@ template class basic_format_arg { public: explicit handle(detail::custom_value custom) : custom_(custom) {} - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { + void format(typename Context::parse_context_type& parse_ctx, Context& ctx) const { custom_.format(custom_.value, parse_ctx, ctx); } @@ -1442,9 +1427,7 @@ template class basic_format_arg { auto type() const -> detail::type { return type_; } auto is_integral() const -> bool { return detail::is_integral_type(type_); } - auto is_arithmetic() const -> bool { - return detail::is_arithmetic_type(type_); - } + auto is_arithmetic() const -> bool { return detail::is_arithmetic_type(type_); } }; /** @@ -1455,42 +1438,43 @@ template class basic_format_arg { \endrst */ template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)) { switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(arg.value_.uint128_value)); + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); } return vis(monostate()); } @@ -1505,28 +1489,30 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; +template +struct void_t_impl { + using type = void; +}; template using void_t = typename detail::void_t_impl::type; #else -template using void_t = void; +template +using void_t = void; #endif template struct is_output_iterator : std::false_type {}; template -struct is_output_iterator< - It, T, - void_t::iterator_category, - decltype(*std::declval() = std::declval())>> +struct is_output_iterator::iterator_category, + decltype(*std::declval() = std::declval())>> : std::true_type {}; template struct is_back_insert_iterator : std::false_type {}; template -struct is_back_insert_iterator> - : std::true_type {}; +struct is_back_insert_iterator> : std::true_type {}; template struct is_contiguous_back_insert_iterator : std::false_type {}; @@ -1543,14 +1529,17 @@ class locale_ref { public: constexpr locale_ref() : locale_(nullptr) {} - template explicit locale_ref(const Locale& loc); + template + explicit locale_ref(const Locale& loc); explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } - template auto get() const -> Locale; + template + auto get() const -> Locale; }; -template constexpr auto encode_types() -> unsigned long long { +template +constexpr auto encode_types() -> unsigned long long { return 0; } @@ -1571,26 +1560,24 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { // The type template parameter is there to avoid an ODR violation when using // a fallback formatter in one translation unit and an implicit conversion in // another (not recommended). -template +template FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value { const auto& arg = arg_mapper().map(val); - static_assert( - !std::is_same::value, - "Cannot format an argument. To make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); + static_assert(!std::is_same::value, + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); return {arg}; } -template +template inline auto make_arg(const T& value) -> basic_format_arg { return make_arg(value); } FMT_END_DETAIL_NAMESPACE // Formatting context. -template class basic_format_context { +template +class basic_format_context { public: /** The character type for the output. */ using char_type = Char; @@ -1604,7 +1591,8 @@ template class basic_format_context { using iterator = OutputIt; using format_arg = basic_format_arg; using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; + template + using formatter_type = formatter; basic_format_context(basic_format_context&&) = default; basic_format_context(const basic_format_context&) = delete; @@ -1613,9 +1601,9 @@ template class basic_format_context { Constructs a ``basic_format_context`` object. References to the arguments are stored in the object so make sure they have appropriate lifetimes. */ - constexpr basic_format_context( - OutputIt out, basic_format_args ctx_args, - detail::locale_ref loc = detail::locale_ref()) + constexpr basic_format_context(OutputIt out, + basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) : out_(out), args_(ctx_args), loc_(loc) {} constexpr auto arg(int id) const -> format_arg { return args_.get(id); } @@ -1625,9 +1613,7 @@ template class basic_format_context { FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { return args_.get_id(name); } - auto args() const -> const basic_format_args& { - return args_; - } + auto args() const -> const basic_format_args& { return args_; } FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } void on_error(const char* message) { error_handler().on_error(message); } @@ -1644,20 +1630,18 @@ template class basic_format_context { }; template -using buffer_context = - basic_format_context, Char>; +using buffer_context = basic_format_context, Char>; using format_context = buffer_context; // Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. -#define FMT_BUFFER_CONTEXT(Char) \ - basic_format_context, Char> +#define FMT_BUFFER_CONTEXT(Char) basic_format_context, Char> template -using is_formattable = bool_constant< - !std::is_same>().map( - std::declval())), - detail::unformattable>::value && - !detail::has_fallback_formatter::value>; +using is_formattable = + bool_constant>().map( + std::declval())), + detail::unformattable>::value && + !detail::has_fallback_formatter::value>; /** \rst @@ -1678,11 +1662,10 @@ class format_arg_store static const size_t num_named_args = detail::count_named_args(); static const bool is_packed = num_args <= detail::max_packed_args; - using value_type = conditional_t, - basic_format_arg>; + using value_type = + conditional_t, basic_format_arg>; - detail::arg_data + detail::arg_data data_; friend class basic_format_args; @@ -1690,9 +1673,8 @@ class format_arg_store static constexpr unsigned long long desc = (is_packed ? detail::encode_types() : detail::is_unpacked_bit | num_args) | - (num_named_args != 0 - ? static_cast(detail::has_named_args_bit) - : 0); + (num_named_args != 0 ? static_cast(detail::has_named_args_bit) + : 0); public: FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args) @@ -1700,9 +1682,9 @@ class format_arg_store #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args(*this), #endif - data_{detail::make_arg< - is_packed, Context, - detail::mapped_type_constant::value>(args)...} { + data_{detail::make_arg::value>( + args)...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1748,7 +1730,8 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg { format_args args = make_format_args(42); // Error: dangling reference \endrst */ -template class basic_format_args { +template +class basic_format_args { public: using size_type = int; using format_arg = basic_format_arg; @@ -1797,10 +1780,8 @@ template class basic_format_args { \endrst */ template - constexpr FMT_INLINE basic_format_args( - const format_arg_store& store) - : basic_format_args(format_arg_store::desc, - store.data_.args()) {} + constexpr FMT_INLINE basic_format_args(const format_arg_store& store) + : basic_format_args(format_arg_store::desc, store.data_.args()) {} /** \rst @@ -1808,8 +1789,7 @@ template class basic_format_args { `~fmt::dynamic_format_arg_store`. \endrst */ - constexpr FMT_INLINE basic_format_args( - const dynamic_format_arg_store& store) + constexpr FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) : basic_format_args(store.get_types(), store.data()) {} /** @@ -1818,8 +1798,7 @@ template class basic_format_args { \endrst */ constexpr basic_format_args(const format_arg* args, int count) - : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), - args) {} + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), args) {} /** Returns the argument with the specified id. */ FMT_CONSTEXPR auto get(int id) const -> format_arg { @@ -1844,8 +1823,7 @@ template class basic_format_args { template auto get_id(basic_string_view name) const -> int { if (!has_named_args()) return -1; - const auto& named_args = - (is_packed() ? values_[-1] : args_[-1].value_).named_args; + const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; for (size_t i = 0; i < named_args.size; ++i) { if (named_args.data[i].name == name) return named_args.data[i].id; } @@ -1854,8 +1832,7 @@ template class basic_format_args { auto max_size() const -> int { unsigned long long max_packed = detail::max_packed_args; - return static_cast(is_packed() ? max_packed - : desc_ & ~detail::is_unpacked_bit); + return static_cast(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit); } }; @@ -1880,7 +1857,8 @@ FMT_BEGIN_DETAIL_NAMESPACE void throw_format_error(const char* message); // Workaround an array initialization issue in gcc 4.8. -template struct fill_t { +template +struct fill_t { private: enum { max_size = 4 }; Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; @@ -1905,7 +1883,8 @@ template struct fill_t { FMT_END_DETAIL_NAMESPACE // Format specifiers for built-in and string types. -template struct basic_format_specs { +template +struct basic_format_specs { int width; int precision; char type; @@ -1932,11 +1911,11 @@ FMT_BEGIN_DETAIL_NAMESPACE enum class arg_id_kind { none, index, name }; // An argument reference. -template struct arg_ref { +template +struct arg_ref { FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} - FMT_CONSTEXPR explicit arg_ref(int index) - : kind(arg_id_kind::index), val(index) {} + FMT_CONSTEXPR explicit arg_ref(int index) : kind(arg_id_kind::index), val(index) {} FMT_CONSTEXPR explicit arg_ref(basic_string_view name) : kind(arg_id_kind::name), val(name) {} @@ -1968,21 +1947,18 @@ struct dynamic_format_specs : basic_format_specs { struct auto_id {}; // A format specifier handler that sets fields in basic_format_specs. -template class specs_setter { +template +class specs_setter { protected: basic_format_specs& specs_; public: - explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) - : specs_(specs) {} + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) : specs_(specs) {} - FMT_CONSTEXPR specs_setter(const specs_setter& other) - : specs_(other.specs_) {} + FMT_CONSTEXPR specs_setter(const specs_setter& other) : specs_(other.specs_) {} FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } - FMT_CONSTEXPR void on_fill(basic_string_view fill) { - specs_.fill = fill; - } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { specs_.fill = fill; } FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; } FMT_CONSTEXPR void on_hash() { specs_.alt = true; } FMT_CONSTEXPR void on_localized() { specs_.localized = true; } @@ -1993,21 +1969,16 @@ template class specs_setter { } FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } - FMT_CONSTEXPR void on_precision(int precision) { - specs_.precision = precision; - } + FMT_CONSTEXPR void on_precision(int precision) { specs_.precision = precision; } FMT_CONSTEXPR void end_precision() {} - FMT_CONSTEXPR void on_type(Char type) { - specs_.type = static_cast(type); - } + FMT_CONSTEXPR void on_type(Char type) { specs_.type = static_cast(type); } }; // Format spec handler that saves references to arguments representing dynamic // width and precision to be resolved at formatting time. template -class dynamic_specs_handler - : public specs_setter { +class dynamic_specs_handler : public specs_setter { public: using char_type = typename ParseContext::char_type; @@ -2016,21 +1987,19 @@ class dynamic_specs_handler : specs_setter(specs), specs_(specs), context_(ctx) {} FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other) - : specs_setter(other), - specs_(other.specs_), - context_(other.context_) {} + : specs_setter(other), specs_(other.specs_), context_(other.context_) {} - template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + template + FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { specs_.width_ref = make_arg_ref(arg_id); } - template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + template + FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { specs_.precision_ref = make_arg_ref(arg_id); } - FMT_CONSTEXPR void on_error(const char* message) { - context_.on_error(message); - } + FMT_CONSTEXPR void on_error(const char* message) { context_.on_error(message); } private: dynamic_format_specs& specs_; @@ -2047,8 +2016,7 @@ class dynamic_specs_handler return arg_ref_type(context_.next_arg_id()); } - FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) - -> arg_ref_type { + FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) -> arg_ref_type { context_.check_arg_id(arg_id); basic_string_view format_str( context_.begin(), to_unsigned(context_.end() - context_.begin())); @@ -2056,7 +2024,8 @@ class dynamic_specs_handler } }; -template constexpr bool is_ascii_letter(Char c) { +template +constexpr bool is_ascii_letter(Char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } @@ -2066,8 +2035,7 @@ constexpr auto to_ascii(Char value) -> Char { return value; } template ::value)> -constexpr auto to_ascii(Char value) -> - typename std::underlying_type::type { +constexpr auto to_ascii(Char value) -> typename std::underlying_type::type { return value; } @@ -2096,8 +2064,7 @@ FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { template <> inline auto find(const char* first, const char* last, char value, const char*& out) -> bool { - out = static_cast( - std::memchr(first, value, to_unsigned(last - first))); + out = static_cast(std::memchr(first, value, to_unsigned(last - first))); return out != nullptr; } @@ -2116,8 +2083,7 @@ FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, } while (p != end && '0' <= *p && *p <= '9'); auto num_digits = p - begin; begin = p; - if (num_digits <= std::numeric_limits::digits10) - return static_cast(value); + if (num_digits <= std::numeric_limits::digits10) return static_cast(value); // Check for overflow. const unsigned max = to_unsigned((std::numeric_limits::max)()); return num_digits == std::numeric_limits::digits10 + 1 && @@ -2128,31 +2094,30 @@ FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, // Parses fill and alignment. template -FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, Handler&& handler) + -> const Char* { FMT_ASSERT(begin != end, ""); auto align = align::none; auto p = begin + code_point_length(begin); if (p >= end) p = begin; for (;;) { switch (to_ascii(*p)) { - case '<': - align = align::left; - break; - case '>': - align = align::right; - break; - case '^': - align = align::center; - break; - default: - break; + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + default: + break; } if (align != align::none) { if (p != begin) { auto c = *begin; - if (c == '{') - return handler.on_error("invalid fill character '{'"), begin; + if (c == '{') return handler.on_error("invalid fill character '{'"), begin; handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); begin = p + 1; } else @@ -2167,7 +2132,8 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, return begin; } -template FMT_CONSTEXPR bool is_name_start(Char c) { +template +FMT_CONSTEXPR bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } @@ -2179,8 +2145,7 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, if (c >= '0' && c <= '9') { int index = 0; if (c != '0') - index = - parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); + index = parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) @@ -2211,8 +2176,8 @@ FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, } template -FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, Handler&& handler) + -> const Char* { using detail::auto_id; struct width_adapter { Handler& handler; @@ -2245,8 +2210,8 @@ FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, } template -FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, Handler&& handler) + -> const Char* { using detail::auto_id; struct precision_adapter { Handler& handler; @@ -2271,8 +2236,7 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, handler.on_error("number is too big"); } else if (c == '{') { ++begin; - if (begin != end) - begin = parse_arg_id(begin, end, precision_adapter{handler}); + if (begin != end) begin = parse_arg_id(begin, end, precision_adapter{handler}); if (begin == end || *begin++ != '}') return handler.on_error("invalid format string"), begin; } else { @@ -2285,12 +2249,9 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, // Parses standard format specifiers and sends notifications about parsed // components to handler. template -FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, - const Char* end, - SpecHandler&& handler) - -> const Char* { - if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && - *begin != 'L') { +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, const Char* end, + SpecHandler&& handler) -> const Char* { + if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L') { handler.on_type(*begin++); return begin; } @@ -2302,20 +2263,20 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, // Parse sign. switch (to_ascii(*begin)) { - case '+': - handler.on_sign(sign::plus); - ++begin; - break; - case '-': - handler.on_sign(sign::minus); - ++begin; - break; - case ' ': - handler.on_sign(sign::space); - ++begin; - break; - default: - break; + case '+': + handler.on_sign(sign::plus); + ++begin; + break; + case '-': + handler.on_sign(sign::minus); + ++begin; + break; + case ' ': + handler.on_sign(sign::space); + ++begin; + break; + default: + break; } if (begin == end) return begin; @@ -2390,8 +2351,8 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, } template -FMT_CONSTEXPR FMT_INLINE void parse_format_string( - basic_string_view format_str, Handler&& handler) { +FMT_CONSTEXPR FMT_INLINE void parse_format_string(basic_string_view format_str, + Handler&& handler) { // this is most likely a name-lookup defect in msvc's modules implementation using detail::find; @@ -2443,13 +2404,12 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( } template -FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) - -> decltype(ctx.begin()) { +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -> decltype(ctx.begin()) { using char_type = typename ParseContext::char_type; using context = buffer_context; - using mapped_type = conditional_t< - mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), T>; + using mapped_type = + conditional_t::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), T>; auto f = conditional_t::value, formatter, fallback_formatter>(); @@ -2461,8 +2421,7 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) // and would be redundant since argument ids are checked when arguments are // retrieved anyway. template -class compile_parse_context - : public basic_format_parse_context { +class compile_parse_context : public basic_format_parse_context { private: int num_args_; using base = basic_format_parse_context; @@ -2489,18 +2448,18 @@ class compile_parse_context template FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { switch (spec) { - case 0: - case 'd': - case 'x': - case 'X': - case 'b': - case 'B': - case 'o': - case 'c': - break; - default: - eh.on_error("invalid type specifier"); - break; + case 0: + case 'd': + case 'x': + case 'X': + case 'b': + case 'B': + case 'o': + case 'c': + break; + default: + eh.on_error("invalid type specifier"); + break; } } @@ -2538,51 +2497,49 @@ struct float_specs { template FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, - ErrorHandler&& eh = {}) - -> float_specs { + ErrorHandler&& eh = {}) -> float_specs { auto result = float_specs(); result.showpoint = specs.alt; result.locale = specs.localized; switch (specs.type) { - case 0: - result.format = float_format::general; - break; - case 'G': - result.upper = true; - FMT_FALLTHROUGH; - case 'g': - result.format = float_format::general; - break; - case 'E': - result.upper = true; - FMT_FALLTHROUGH; - case 'e': - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case 'F': - result.upper = true; - FMT_FALLTHROUGH; - case 'f': - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case 'A': - result.upper = true; - FMT_FALLTHROUGH; - case 'a': - result.format = float_format::hex; - break; - default: - eh.on_error("invalid type specifier"); - break; + case 0: + result.format = float_format::general; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; + default: + eh.on_error("invalid type specifier"); + break; } return result; } template -FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {}) - -> bool { +FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {}) -> bool { if (spec == 0 || spec == 's') return true; if (spec != 'p') eh.on_error("invalid type specifier"); return false; @@ -2600,7 +2557,8 @@ FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { // A parse_format_specs handler that checks if specifiers are consistent with // the argument type. -template class specs_checker : public Handler { +template +class specs_checker : public Handler { private: detail::type arg_type_; @@ -2656,8 +2614,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { if (name == T::name) return N; } - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name(name); + if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name(name); (void)name; // Workaround an MSVC bug about "unused" parameter. return invalid_arg_index; } @@ -2666,8 +2623,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name<0, Args...>(name); + if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); #endif (void)name; return invalid_arg_index; @@ -2686,17 +2642,15 @@ class format_string_checker { parse_func parse_funcs_[num_args > 0 ? num_args : 1]; public: - explicit FMT_CONSTEXPR format_string_checker( - basic_string_view format_str, ErrorHandler eh) + explicit FMT_CONSTEXPR format_string_checker(basic_string_view format_str, + ErrorHandler eh) : context_(format_str, num_args, eh), parse_funcs_{&parse_format_specs...} {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return context_.check_arg_id(id), id; - } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS auto index = get_arg_index_by_name(id); @@ -2718,13 +2672,10 @@ class format_string_checker { return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; } - FMT_CONSTEXPR void on_error(const char* message) { - context_.on_error(message); - } + FMT_CONSTEXPR void on_error(const char* message) { context_.on_error(message); } }; -template ::value), int>> +template ::value), int>> void check_format_string(S format_str) { FMT_CONSTEXPR auto s = to_string_view(format_str); using checker = format_string_checker -void vformat_to( - buffer& buf, basic_string_view fmt, - basic_format_args)> args, - detail::locale_ref loc = {}); +void vformat_to(buffer& buf, basic_string_view fmt, + basic_format_args)> args, + detail::locale_ref loc = {}); FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); #ifndef _WIN32 @@ -2749,9 +2699,9 @@ FMT_END_DETAIL_NAMESPACE // A formatter specialization for the core types corresponding to detail::type // constants. template -struct formatter::value != - detail::type::custom_type>> { +struct formatter< + T, Char, + enable_if_t::value != detail::type::custom_type>> { private: detail::dynamic_format_specs specs_; @@ -2764,59 +2714,58 @@ struct formatter; auto type = detail::type_constant::value; - auto checker = - detail::specs_checker(handler_type(specs_, ctx), type); + auto checker = detail::specs_checker(handler_type(specs_, ctx), type); auto it = detail::parse_format_specs(begin, end, checker); auto eh = ctx.error_handler(); switch (type) { - case detail::type::none_type: - FMT_ASSERT(false, "invalid argument type"); - break; - case detail::type::bool_type: - if (!specs_.type || specs_.type == 's') break; - FMT_FALLTHROUGH; - case detail::type::int_type: - case detail::type::uint_type: - case detail::type::long_long_type: - case detail::type::ulong_long_type: - case detail::type::int128_type: - case detail::type::uint128_type: - detail::check_int_type_spec(specs_.type, eh); - break; - case detail::type::char_type: - detail::check_char_specs(specs_, eh); - break; - case detail::type::float_type: - if (detail::const_check(FMT_USE_FLOAT)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "float support disabled"); - break; - case detail::type::double_type: - if (detail::const_check(FMT_USE_DOUBLE)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "double support disabled"); - break; - case detail::type::long_double_type: - if (detail::const_check(FMT_USE_LONG_DOUBLE)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "long double support disabled"); - break; - case detail::type::cstring_type: - detail::check_cstring_type_spec(specs_.type, eh); - break; - case detail::type::string_type: - detail::check_string_type_spec(specs_.type, eh); - break; - case detail::type::pointer_type: - detail::check_pointer_type_spec(specs_.type, eh); - break; - case detail::type::custom_type: - // Custom format specifiers are checked in parse functions of - // formatter specializations. - break; + case detail::type::none_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case detail::type::bool_type: + if (!specs_.type || specs_.type == 's') break; + FMT_FALLTHROUGH; + case detail::type::int_type: + case detail::type::uint_type: + case detail::type::long_long_type: + case detail::type::ulong_long_type: + case detail::type::int128_type: + case detail::type::uint128_type: + detail::check_int_type_spec(specs_.type, eh); + break; + case detail::type::char_type: + detail::check_char_specs(specs_, eh); + break; + case detail::type::float_type: + if (detail::const_check(FMT_USE_FLOAT)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "float support disabled"); + break; + case detail::type::double_type: + if (detail::const_check(FMT_USE_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "double support disabled"); + break; + case detail::type::long_double_type: + if (detail::const_check(FMT_USE_LONG_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "long double support disabled"); + break; + case detail::type::cstring_type: + detail::check_cstring_type_spec(specs_.type, eh); + break; + case detail::type::string_type: + detail::check_string_type_spec(specs_.type, eh); + break; + case detail::type::pointer_type: + detail::check_pointer_type_spec(specs_.type, eh); + break; + case detail::type::custom_type: + // Custom format specifiers are checked in parse functions of + // formatter specializations. + break; } return it; } @@ -2826,21 +2775,23 @@ struct formatter decltype(ctx.out()); }; -template struct basic_runtime { basic_string_view str; }; +template +struct basic_runtime { + basic_string_view str; +}; -template class basic_format_string { +template +class basic_format_string { private: basic_string_view str_; public: template >::value)> + FMT_ENABLE_IF(std::is_convertible>::value)> FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) { static_assert( - detail::count< - (std::is_base_of>::value && - std::is_reference::value)...>() == 0, + detail::count<(std::is_base_of>::value && + std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); #ifdef FMT_HAS_CONSTEVAL if constexpr (detail::count_named_args() == 0) { @@ -2859,15 +2810,18 @@ template class basic_format_string { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. -template using format_string = string_view; -template auto runtime(const S& s) -> basic_string_view> { +template +using format_string = string_view; +template +auto runtime(const S& s) -> basic_string_view> { return s; } #else template using format_string = basic_format_string...>; // Creates a runtime format string. -template auto runtime(const S& s) -> basic_runtime> { +template +auto runtime(const S& s) -> basic_runtime> { return {{s}}; } #endif @@ -2919,7 +2873,8 @@ FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) return vformat_to(out, fmt, fmt::make_format_args(args...)); } -template struct format_to_n_result { +template +struct format_to_n_result { /** Iterator past the end of the output range. */ OutputIt out; /** Total (not truncated) output size. */ @@ -2930,8 +2885,7 @@ template ::value)> auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -> format_to_n_result { - using buffer = - detail::iterator_buffer; + using buffer = detail::iterator_buffer; auto buf = buffer(out, n); detail::vformat_to(buf, fmt, args); return {buf.out(), buf.count()}; @@ -3001,6 +2955,6 @@ FMT_GCC_PRAGMA("GCC pop_options") FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY -# include "format.h" +#include "format.h" #endif #endif // FMT_CORE_H_ diff --git a/inst/include/fmt/format-inl.h b/inst/include/fmt/format-inl.h index a802aea5..5e10ae2a 100644 --- a/inst/include/fmt/format-inl.h +++ b/inst/include/fmt/format-inl.h @@ -19,11 +19,11 @@ #include #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -# include +#include #endif #ifdef _WIN32 -# include // _isatty +#include // _isatty #endif #include "format.h" @@ -41,7 +41,7 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { } #ifndef _MSC_VER -# define FMT_SNPRINTF snprintf +#define FMT_SNPRINTF snprintf #else // _MSC_VER inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { va_list args; @@ -50,7 +50,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { va_end(args); return result; } -# define FMT_SNPRINTF fmt_snprintf +#define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER FMT_FUNC void format_error_code(detail::buffer& out, int error_code, @@ -86,8 +86,7 @@ FMT_FUNC void report_error(format_func func, int error_code, } // A wrapper around fwrite that throws on error. -inline void fwrite_fully(const void* ptr, size_t size, size_t count, - FILE* stream) { +inline void fwrite_fully(const void* ptr, size_t size, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, size, count, stream); if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); } @@ -98,7 +97,8 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { static_assert(std::is_same::value, ""); } -template Locale locale_ref::get() const { +template +Locale locale_ref::get() const { static_assert(std::is_same::value, ""); return locale_ ? *static_cast(locale_) : std::locale(); } @@ -110,16 +110,17 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } -template FMT_FUNC Char decimal_point_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .decimal_point(); +template +FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()).decimal_point(); } #else template FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; } -template FMT_FUNC Char decimal_point_impl(locale_ref) { +template +FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; } #endif @@ -137,7 +138,8 @@ FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, namespace detail { -template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { +template <> +FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { // fallback_uintptr is always stored in little endian. int i = static_cast(sizeof(void*)) - 1; while (i > 0 && n.value[i] == 0) --i; @@ -146,22 +148,29 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { } #if __cplusplus < 201703L -template constexpr const char basic_data::digits[][2]; -template constexpr const char basic_data::hex_digits[]; -template constexpr const char basic_data::signs[]; -template constexpr const unsigned basic_data::prefixes[]; -template constexpr const char basic_data::left_padding_shifts[]; +template +constexpr const char basic_data::digits[][2]; +template +constexpr const char basic_data::hex_digits[]; +template +constexpr const char basic_data::signs[]; +template +constexpr const unsigned basic_data::prefixes[]; +template +constexpr const char basic_data::left_padding_shifts[]; template constexpr const char basic_data::right_padding_shifts[]; #endif -template struct bits { +template +struct bits { static FMT_CONSTEXPR_DECL const int value = static_cast(sizeof(T) * std::numeric_limits::digits); }; class fp; -template fp normalize(fp value); +template +fp normalize(fp value); // Lower (upper) boundary is a value half way between a floating-point value // and its predecessor (successor). Boundaries have the same exponent as the @@ -189,17 +198,18 @@ class fp { // normalized form. static FMT_CONSTEXPR_DECL const int double_significand_size = std::numeric_limits::digits - 1; - static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = - 1ULL << double_significand_size; - static FMT_CONSTEXPR_DECL const int significand_size = - bits::value; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = 1ULL << double_significand_size; + static FMT_CONSTEXPR_DECL const int significand_size = bits::value; fp() : f(0), e(0) {} fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} // Constructs fp from an IEEE754 double. It is a template to prevent compile // errors on platforms where double is not IEEE754. - template explicit fp(Double d) { assign(d); } + template + explicit fp(Double d) { + assign(d); + } // Assigns d to this and return true iff predecessor is closer than successor. template ::value)> @@ -216,8 +226,7 @@ class fp { constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); auto u = bit_cast>(d); f = u & significand_mask; - int biased_e = - static_cast((u & exponent_mask) >> float_significand_size); + int biased_e = static_cast((u & exponent_mask) >> float_significand_size); // Predecessor is closer if d is a normalized power of 2 (f == 0) other than // the smallest normalized number (biased_e > 1). bool is_predecessor_closer = f == 0 && biased_e > 1; @@ -237,7 +246,8 @@ class fp { }; // Normalizes the value converted from double and multiplied by (1 << SHIFT). -template fp normalize(fp value) { +template +fp normalize(fp value) { // Handle subnormals. const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; while ((value.f & shifted_implicit_bit) == 0) { @@ -245,8 +255,7 @@ template fp normalize(fp value) { --value.e; } // Subtract 1 to account for hidden bit. - const auto offset = - fp::significand_size - fp::double_significand_size - SHIFT - 1; + const auto offset = fp::significand_size - fp::double_significand_size - SHIFT - 1; value.f <<= offset; value.e -= offset; return value; @@ -280,34 +289,27 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) { // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. // These are generated by support/compute-powers.py. static constexpr const uint64_t pow10_significands[] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, + 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, + 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637, + 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5, + 0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, + 0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd, + 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3, + 0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, + 0x9c40000000000000, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245, + 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a, + 0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, + 0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, + 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a, + 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, + 0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, }; @@ -419,8 +421,7 @@ class bigint { double_bigit carry = 0; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { double_bigit result = bigits_[i] * lower + (carry & mask); - carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + carry = bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); bigits_[i] = static_cast(result); } while (carry != 0) { @@ -472,7 +473,8 @@ class bigint { return *this; } - template bigint& operator*=(Int value) { + template + bigint& operator*=(Int value) { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; @@ -480,8 +482,7 @@ class bigint { friend int compare(const bigint& lhs, const bigint& rhs) { int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + if (num_lhs_bigits != num_rhs_bigits) return num_lhs_bigits > num_rhs_bigits ? 1 : -1; int i = static_cast(lhs.bigits_.size()) - 1; int j = static_cast(rhs.bigits_.size()) - 1; int end = i - j; @@ -495,8 +496,7 @@ class bigint { } // Returns compare(lhs1 + lhs2, rhs). - friend int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { + friend int add_compare(const bigint& lhs1, const bigint& lhs2, const bigint& rhs) { int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); int num_rhs_bigits = rhs.num_bigits(); if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; @@ -556,8 +556,7 @@ class bigint { sum >>= bits::value; // Compute the carry. } // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index) { for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) sum += static_cast(n[i++]) * n[j--]; (*this)[bigit_index] = static_cast(sum); @@ -605,15 +604,14 @@ enum class round_direction { unknown, up, down }; // error should be less than divisor / 2. inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. // Round down if (remainder + error) * 2 <= divisor. if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) return round_direction::down; // Round up if (remainder - error) * 2 >= divisor. - if (remainder >= error && - remainder - error >= divisor - (remainder - error)) { + if (remainder >= error && remainder - error >= divisor - (remainder - error)) { return round_direction::up; } return round_direction::unknown; @@ -628,9 +626,8 @@ enum result { } inline uint64_t power_of_10_64(int exp) { - static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; + static constexpr const uint64_t data[] = { + 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return data[exp]; } @@ -651,8 +648,8 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, uint64_t fractional = value.f & (one.f - 1); exp = count_digits(integral); // kappa in Grisu. // Divide by 10 to prevent overflow. - auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e, - value.f / 10, error * 10, exp); + auto result = + handler.on_start(power_of_10_64(exp - 1) << -one.e, value.f / 10, error * 10, exp); if (result != digits::more) return result; // Generate digits for the integral part. This can produce up to 10 digits. do { @@ -664,45 +661,44 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, // This optimization by Milo Yip reduces the number of integer divisions by // one per iteration. switch (exp) { - case 10: - divmod_integral(1000000000); - break; - case 9: - divmod_integral(100000000); - break; - case 8: - divmod_integral(10000000); - break; - case 7: - divmod_integral(1000000); - break; - case 6: - divmod_integral(100000); - break; - case 5: - divmod_integral(10000); - break; - case 4: - divmod_integral(1000); - break; - case 3: - divmod_integral(100); - break; - case 2: - divmod_integral(10); - break; - case 1: - digit = integral; - integral = 0; - break; - default: - FMT_ASSERT(false, "invalid number of digits"); + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); } --exp; auto remainder = (static_cast(integral) << -one.e) + fractional; result = handler.on_digit(static_cast('0' + digit), - power_of_10_64(exp) << -one.e, remainder, error, - exp, true); + power_of_10_64(exp) << -one.e, remainder, error, exp, true); if (result != digits::more) return result; } while (exp > 0); // Generate digits for the fractional part. @@ -783,14 +779,11 @@ struct uint128_wrapper { uint128_t internal_; constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT - : internal_{static_cast(low) | - (static_cast(high) << 64)} {} + : internal_{static_cast(low) | (static_cast(high) << 64)} {} constexpr uint128_wrapper(uint128_t u) : internal_{u} {} - constexpr uint64_t high() const FMT_NOEXCEPT { - return uint64_t(internal_ >> 64); - } + constexpr uint64_t high() const FMT_NOEXCEPT { return uint64_t(internal_ >> 64); } constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { @@ -801,24 +794,23 @@ struct uint128_wrapper { uint64_t high_; uint64_t low_; - constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT - : high_{high}, - low_{low} {} + constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT : high_{high}, + low_{low} {} constexpr uint64_t high() const FMT_NOEXCEPT { return high_; } constexpr uint64_t low() const FMT_NOEXCEPT { return low_; } uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { -# if defined(_MSC_VER) && defined(_M_X64) +#if defined(_MSC_VER) && defined(_M_X64) unsigned char carry = _addcarry_u64(0, low_, n, &low_); _addcarry_u64(carry, high_, 0, &high_); return *this; -# else +#else uint64_t sum = low_ + n; high_ += (sum < low_ ? 1 : 0); low_ = sum; return *this; -# endif +#endif } #endif }; @@ -889,17 +881,14 @@ inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { - return x * y; -} +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { return x * y; } // Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from // https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. inline int floor_log10_pow2(int e) FMT_NOEXCEPT { FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); const int shift = 22; - return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> - shift; + return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> shift; } // Various fast log computations. @@ -908,19 +897,16 @@ inline int floor_log2_pow10(int e) FMT_NOEXCEPT { const uint64_t log2_10_integer_part = 3; const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; const int shift_amount = 19; - return (e * static_cast( - (log2_10_integer_part << shift_amount) | - (log2_10_fractional_digits >> (64 - shift_amount)))) >> + return (e * static_cast((log2_10_integer_part << shift_amount) | + (log2_10_fractional_digits >> (64 - shift_amount)))) >> shift_amount; } inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; const int shift_amount = 22; - return (e * static_cast(data::log10_2_significand >> - (64 - shift_amount)) - - static_cast(log10_4_over_3_fractional_digits >> - (64 - shift_amount))) >> + return (e * static_cast(data::log10_2_significand >> (64 - shift_amount)) - + static_cast(log10_4_over_3_fractional_digits >> (64 - shift_amount))) >> shift_amount; } @@ -945,7 +931,8 @@ inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { } // Table entry type for divisibility test. -template struct divtest_table_entry { +template +struct divtest_table_entry { T mod_inv; T max_quotient; }; @@ -954,41 +941,27 @@ template struct divtest_table_entry { inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { FMT_ASSERT(exp <= 10, "too large exponent"); static constexpr const divtest_table_entry divtest_table[] = { - {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, - {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, - {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, - {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, - {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, - {0x3ed61f49, 0x000001b7}}; + {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, {0xc28f5c29, 0x0a3d70a3}, + {0x26e978d5, 0x020c49ba}, {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, + {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, {0x22e90e21, 0x00002af3}, + {0x3a2e9c6d, 0x00000897}, {0x3ed61f49, 0x000001b7}}; return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; } inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { FMT_ASSERT(exp <= 23, "too large exponent"); static constexpr const divtest_table_entry divtest_table[] = { - {0x0000000000000001, 0xffffffffffffffff}, - {0xcccccccccccccccd, 0x3333333333333333}, - {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, - {0x1cac083126e978d5, 0x020c49ba5e353f7c}, - {0xd288ce703afb7e91, 0x0068db8bac710cb2}, - {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, - {0x790fb65668c26139, 0x000431bde82d7b63}, - {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, - {0xc767074b22e90e21, 0x00002af31dc46118}, - {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, - {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, - {0x0fee64690c913975, 0x00000057f5ff85e5}, - {0x3662e0e1cf503eb1, 0x000000119799812d}, - {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, - {0x54186f653140a659, 0x00000000b424dc35}, - {0x7738164770402145, 0x0000000024075f3d}, - {0xe4a4d1417cd9a041, 0x000000000734aca5}, - {0xc75429d9e5c5200d, 0x000000000170ef54}, - {0xc1773b91fac10669, 0x000000000049c977}, - {0x26b172506559ce15, 0x00000000000ec1e4}, - {0xd489e3a9addec2d1, 0x000000000002f394}, - {0x90e860bb892c8d5d, 0x000000000000971d}, - {0x502e79bf1b6f4f79, 0x0000000000001e39}, - {0xdcd618596be30fe5, 0x000000000000060b}}; + {0x0000000000000001, 0xffffffffffffffff}, {0xcccccccccccccccd, 0x3333333333333333}, + {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, {0x1cac083126e978d5, 0x020c49ba5e353f7c}, + {0xd288ce703afb7e91, 0x0068db8bac710cb2}, {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, + {0x790fb65668c26139, 0x000431bde82d7b63}, {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, + {0xc767074b22e90e21, 0x00002af31dc46118}, {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, + {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, {0x0fee64690c913975, 0x00000057f5ff85e5}, + {0x3662e0e1cf503eb1, 0x000000119799812d}, {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, + {0x54186f653140a659, 0x00000000b424dc35}, {0x7738164770402145, 0x0000000024075f3d}, + {0xe4a4d1417cd9a041, 0x000000000734aca5}, {0xc75429d9e5c5200d, 0x000000000170ef54}, + {0xc1773b91fac10669, 0x000000000049c977}, {0x26b172506559ce15, 0x00000000000ec1e4}, + {0xd489e3a9addec2d1, 0x000000000002f394}, {0x90e860bb892c8d5d, 0x000000000000971d}, + {0x502e79bf1b6f4f79, 0x0000000000001e39}, {0xdcd618596be30fe5, 0x000000000000060b}}; return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; } @@ -1013,7 +986,8 @@ bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { +template +uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { static constexpr struct { uint32_t magic_number; int shift_amount; @@ -1034,9 +1008,11 @@ inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { } // Various subroutines using pow10 cache -template struct cache_accessor; +template +struct cache_accessor; -template <> struct cache_accessor { +template <> +struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; @@ -1044,32 +1020,26 @@ template <> struct cache_accessor { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); constexpr const uint64_t pow10_significands[] = { - 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, - 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, - 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, - 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, - 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, - 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, - 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, - 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, - 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, - 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, - 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, - 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, - 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, - 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, - 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, - 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, - 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, - 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, - 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, - 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, - 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, - 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, - 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, - 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, - 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, - 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, + 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, + 0xc16d9a0095928a28, 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, 0xe69594bec44de15c, + 0x901d7cf73ab0acda, 0xb424dc35095cd810, 0xe12e13424bb40e14, 0x8cbccc096f5088cc, + 0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, 0xd1b71758e219652c, + 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, 0xcccccccccccccccd, 0x8000000000000000, + 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000, + 0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, + 0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, + 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, + 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, + 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, 0x9dc5ada82b70b59d, + 0xc5371912364ce305, 0xf684df56c3e01bc6, 0x9a130b963a6c115c, 0xc097ce7bc90715b3, + 0xf0bdc21abb48db20, 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, 0x8f7e32ce7bea5c6f, + 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; return pow10_significands[k - float_info::min_k]; } @@ -1083,8 +1053,7 @@ template <> struct cache_accessor { return static_cast(cache >> (64 - 1 - beta_minus_1)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, + static bool compute_mul_parity(carrier_uint two_f, const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { FMT_ASSERT(beta_minus_1 >= 1, ""); FMT_ASSERT(beta_minus_1 < 64, ""); @@ -1109,14 +1078,14 @@ template <> struct cache_accessor { static carrier_uint compute_round_up_for_shorter_interval_case( const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return (static_cast( - cache >> - (64 - float_info::significand_bits - 2 - beta_minus_1)) + + cache >> (64 - float_info::significand_bits - 2 - beta_minus_1)) + 1) / 2; } }; -template <> struct cache_accessor { +template <> +struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128_wrapper; @@ -1778,14 +1747,12 @@ template <> struct cache_accessor { return pow10_significands[k - float_info::min_k]; #else static constexpr const uint64_t powers_of_5_64[] = { - 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, - 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, - 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, - 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, - 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, - 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, - 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, - 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, + 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, + 0x000000000005f5e1, 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, 0x000000071afd498d, + 0x0000002386f26fc1, 0x000000b1a2bc2ec5, 0x000003782dace9d9, 0x00001158e460913d, + 0x000056bc75e2d631, 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; static constexpr const uint32_t pow10_recovery_errors[] = { @@ -1815,25 +1782,23 @@ template <> struct cache_accessor { // Try to recover the real cache. uint64_t pow5 = powers_of_5_64[offset]; uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); - uint128_wrapper middle_low = - umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + uint128_wrapper middle_low = umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); recovered_cache += middle_low.high(); uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); - recovered_cache = - uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, - ((middle_low.low() >> alpha) | middle_to_low)}; + recovered_cache = uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; if (kb < 0) recovered_cache += 1; // Get error. int error_idx = (k - float_info::min_k) / 16; - uint32_t error = (pow10_recovery_errors[error_idx] >> - ((k - float_info::min_k) % 16) * 2) & - 0x3; + uint32_t error = + (pow10_recovery_errors[error_idx] >> ((k - float_info::min_k) % 16) * 2) & + 0x3; // Add the error back. FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); @@ -1851,8 +1816,7 @@ template <> struct cache_accessor { return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, + static bool compute_mul_parity(carrier_uint two_f, const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { FMT_ASSERT(beta_minus_1 >= 1, ""); FMT_ASSERT(beta_minus_1 < 64, ""); @@ -1886,15 +1850,12 @@ template <> struct cache_accessor { // Various integer checks template bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { - return exponent >= - float_info< - T>::case_shorter_interval_left_endpoint_lower_threshold && - exponent <= - float_info::case_shorter_interval_left_endpoint_upper_threshold; + return exponent >= float_info::case_shorter_interval_left_endpoint_lower_threshold && + exponent <= float_info::case_shorter_interval_left_endpoint_upper_threshold; } template -bool is_endpoint_integer(typename float_info::carrier_uint two_f, - int exponent, int minus_k) FMT_NOEXCEPT { +bool is_endpoint_integer(typename float_info::carrier_uint two_f, int exponent, + int minus_k) FMT_NOEXCEPT { if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; // For k >= 0. if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; @@ -2061,23 +2022,22 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { // Otherwise, compute the round-up of y ret_value.significand = - cache_accessor::compute_round_up_for_shorter_interval_case( - cache, beta_minus_1); + cache_accessor::compute_round_up_for_shorter_interval_case(cache, beta_minus_1); ret_value.exponent = minus_k; // When tie occurs, choose one of them according to the rule if (exponent >= float_info::shorter_interval_tie_lower_threshold && exponent <= float_info::shorter_interval_tie_upper_threshold) { - ret_value.significand = ret_value.significand % 2 == 0 - ? ret_value.significand - : ret_value.significand - 1; + ret_value.significand = ret_value.significand % 2 == 0 ? ret_value.significand + : ret_value.significand - 1; } else if (ret_value.significand < xi) { ++ret_value.significand; } return ret_value; } -template decimal_fp to_decimal(T x) FMT_NOEXCEPT { +template +decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -2088,8 +2048,8 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { const carrier_uint significand_mask = (static_cast(1) << float_info::significand_bits) - 1; carrier_uint significand = (br & significand_mask); - int exponent = static_cast((br & exponent_mask()) >> - float_info::significand_bits); + int exponent = + static_cast((br & exponent_mask()) >> float_info::significand_bits); if (exponent != 0) { // Check if normal. exponent += float_info::exponent_bias - float_info::significand_bits; @@ -2097,8 +2057,7 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Shorter interval case; proceed like Schubfach. if (significand == 0) return shorter_interval_case(exponent); - significand |= - (static_cast(1) << float_info::significand_bits); + significand |= (static_cast(1) << float_info::significand_bits); } else { // Subnormal case; the interval is always regular. if (significand == 0) return {0, 0}; @@ -2118,8 +2077,7 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); const carrier_uint two_fc = significand << 1; const carrier_uint two_fr = two_fc | 1; - const carrier_uint zi = - cache_accessor::compute_mul(two_fr << beta_minus_1, cache); + const carrier_uint zi = cache_accessor::compute_mul(two_fr << beta_minus_1, cache); // Step 2: Try larger divisor; remove trailing zeros if necessary @@ -2127,8 +2085,8 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // better than the compiler; we are computing zi / big_divisor here decimal_fp ret_value; ret_value.significand = divide_by_10_to_kappa_plus_1(zi); - uint32_t r = static_cast(zi - float_info::big_divisor * - ret_value.significand); + uint32_t r = + static_cast(zi - float_info::big_divisor * ret_value.significand); if (r > deltai) { goto small_divisor_case_label; @@ -2145,8 +2103,7 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Check conditions in the order different from the paper // to take advantage of short-circuiting const carrier_uint two_fl = two_fc - 1; - if ((!include_left_endpoint || - !is_endpoint_integer(two_fl, exponent, minus_k)) && + if ((!include_left_endpoint || !is_endpoint_integer(two_fl, exponent, minus_k)) && !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { goto small_divisor_case_label; } @@ -2168,8 +2125,7 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Is dist divisible by 2^kappa? if ((dist & mask) == 0) { - const bool approx_y_parity = - ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + const bool approx_y_parity = ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; dist >>= float_info::kappa; // Is dist divisible by 5^kappa? @@ -2204,8 +2160,7 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { else { // Since we know dist is small, we might be able to optimize the division // better than the compiler; we are computing dist / small_divisor here - ret_value.significand += - small_division_by_pow10::kappa>(dist); + ret_value.significand += small_division_by_pow10::kappa>(dist); } return ret_value; } @@ -2283,8 +2238,7 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, } else if (high) { int result = add_compare(numerator, numerator, denominator); // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; + if (result > 0 || (result == 0 && (digit % 2) != 0)) ++data[num_digits - 1]; } buf.try_resize(to_unsigned(num_digits)); exp10 -= num_digits - 1; @@ -2367,8 +2321,8 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { const int min_exp = -60; // alpha in Grisu. int cached_exp10 = 0; // K in Grisu. fp normalized = normalize(fp(value)); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::significand_size), cached_exp10); + const auto cached_pow = + get_cached_power(min_exp - (normalized.e + fp::significand_size), cached_exp10); normalized = normalized * cached_pow; // Limit precision to the maximum possible number of significant digits in an // IEEE754 double because we don't need to generate zeros. @@ -2395,16 +2349,14 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { } // namespace detail template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf) { +int snprintf_float(T value, int precision, float_specs specs, buffer& buf) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); static_assert(!std::is_same::value, ""); // Subtract 1 to account for the difference in precision since we use %e for // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) + if (specs.format == float_format::general || specs.format == float_format::exp) precision = (precision >= 0 ? precision : 6) - 1; // Build the format string. @@ -2430,15 +2382,13 @@ int snprintf_float(T value, int precision, float_specs specs, auto capacity = buf.capacity() - offset; #ifdef FMT_FUZZ if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); + throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf"); #endif // Suppress the warning about a nonliteral format string. // Cannot use auto because of a bug in MinGW (#1532). int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); + int result = precision >= 0 ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); if (result < 0) { // The buffer will grow exponentially. buf.try_reserve(buf.capacity() + 1); @@ -2499,14 +2449,13 @@ int snprintf_float(T value, int precision, float_specs specs, } } // namespace detail -template <> struct formatter { - FMT_CONSTEXPR format_parse_context::iterator parse( - format_parse_context& ctx) { +template <> +struct formatter { + FMT_CONSTEXPR format_parse_context::iterator parse(format_parse_context& ctx) { return ctx.begin(); } - format_context::iterator format(const detail::bigint& n, - format_context& ctx) { + format_context::iterator format(const detail::bigint& n, format_context& ctx) { auto out = ctx.out(); bool first = true; for (auto i = n.bigits_.size(); i > 0; --i) { @@ -2519,8 +2468,7 @@ template <> struct formatter { out = format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) - out = format_to(out, FMT_STRING("p{}"), - n.exp_ * detail::bigint::bigit_bits); + out = format_to(out, FMT_STRING("p{}"), n.exp_ * detail::bigint::bigit_bits); return out; } }; @@ -2554,8 +2502,7 @@ FMT_FUNC void detail::error_handler::on_error(const char* message) { FMT_THROW(format_error(message)); } -FMT_FUNC void report_system_error(int error_code, - const char* message) FMT_NOEXCEPT { +FMT_FUNC void report_system_error(int error_code, const char* message) FMT_NOEXCEPT { report_error(format_system_error, error_code, message); } @@ -2582,9 +2529,8 @@ FMT_FUNC void print(std::FILE* f, string_view text) { if (_isatty(fd)) { detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); auto written = detail::dword(); - if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), - u16.c_str(), static_cast(u16.size()), - &written, nullptr)) { + if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), + static_cast(u16.size()), &written, nullptr)) { return; } // Fallback to fwrite on failure. It can happen if the output has been @@ -2606,8 +2552,7 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, format_args args) { memory_buffer buffer; - detail::vformat_to(buffer, format_str, - basic_format_args>(args)); + detail::vformat_to(buffer, format_str, basic_format_args>(args)); fwrite_fully(buffer.data(), 1, buffer.size(), f); } #endif diff --git a/inst/include/fmt/format.h b/inst/include/fmt/format.h index 44bb39b3..01d8365e 100644 --- a/inst/include/fmt/format.h +++ b/inst/include/fmt/format.h @@ -44,43 +44,44 @@ #include "core.h" #ifdef __INTEL_COMPILER -# define FMT_ICC_VERSION __INTEL_COMPILER +#define FMT_ICC_VERSION __INTEL_COMPILER #elif defined(__ICL) -# define FMT_ICC_VERSION __ICL +#define FMT_ICC_VERSION __ICL #else -# define FMT_ICC_VERSION 0 +#define FMT_ICC_VERSION 0 #endif #ifdef __NVCC__ -# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) #else -# define FMT_CUDA_VERSION 0 +#define FMT_CUDA_VERSION 0 #endif #ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#define FMT_HAS_BUILTIN(x) __has_builtin(x) #else -# define FMT_HAS_BUILTIN(x) 0 +#define FMT_HAS_BUILTIN(x) 0 #endif #if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_NOINLINE __attribute__((noinline)) +#define FMT_NOINLINE __attribute__((noinline)) #else -# define FMT_NOINLINE +#define FMT_NOINLINE #endif #if FMT_MSC_VER -# define FMT_MSC_DEFAULT = default +#define FMT_MSC_DEFAULT = default #else -# define FMT_MSC_DEFAULT +#define FMT_MSC_DEFAULT #endif #ifndef FMT_THROW -# if FMT_EXCEPTIONS -# if FMT_MSC_VER || FMT_NVCC +#if FMT_EXCEPTIONS +#if FMT_MSC_VER || FMT_NVCC FMT_BEGIN_NAMESPACE namespace detail { -template inline void do_throw(const Exception& x) { +template +inline void do_throw(const Exception& x) { // Silence unreachable code warnings in MSVC and NVCC because these // are nearly impossible to fix in a generic code. volatile bool b = true; @@ -88,56 +89,56 @@ template inline void do_throw(const Exception& x) { } } // namespace detail FMT_END_NAMESPACE -# define FMT_THROW(x) detail::do_throw(x) -# else -# define FMT_THROW(x) throw x -# endif -# else -# define FMT_THROW(x) \ - do { \ - FMT_ASSERT(false, (x).what()); \ - } while (false) -# endif +#define FMT_THROW(x) detail::do_throw(x) +#else +#define FMT_THROW(x) throw x +#endif +#else +#define FMT_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ + } while (false) +#endif #endif #if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) +#define FMT_TRY try +#define FMT_CATCH(x) catch (x) #else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) +#define FMT_TRY if (true) +#define FMT_CATCH(x) if (false) #endif #ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif +#if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +#define FMT_DEPRECATED [[deprecated]] +#else +#if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +#define FMT_DEPRECATED __attribute__((deprecated)) +#elif FMT_MSC_VER +#define FMT_DEPRECATED __declspec(deprecated) +#else +#define FMT_DEPRECATED /* deprecated */ +#endif +#endif #endif // Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. #if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS +#define FMT_DEPRECATED_ALIAS #else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#define FMT_DEPRECATED_ALIAS FMT_DEPRECATED #endif #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) -# define FMT_USE_USER_DEFINED_LITERALS 1 -# else -# define FMT_USE_USER_DEFINED_LITERALS 0 -# endif +#if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +#define FMT_USE_USER_DEFINED_LITERALS 1 +#else +#define FMT_USE_USER_DEFINED_LITERALS 0 +#endif #endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of @@ -145,26 +146,26 @@ FMT_END_NAMESPACE // largest integer type. This results in a reduction in binary size but will // cause a decrease in integer formatting performance. #if !defined(FMT_REDUCE_INT_INSTANTIATIONS) -# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#define FMT_BUILTIN_CLZ(n) __builtin_clz(n) #endif #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) #endif #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +#define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) #endif #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +#define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) #endif #if FMT_MSC_VER -# include // _BitScanReverse[64], _BitScanForward[64], _umul128 +#include // _BitScanReverse[64], _BitScanForward[64], _umul128 #endif // Some compilers masquerade as both MSVC and GCC-likes or otherwise support @@ -174,15 +175,15 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# if !defined(__clang__) -# pragma managed(push, off) -# pragma intrinsic(_BitScanForward) -# pragma intrinsic(_BitScanReverse) -# if defined(_WIN64) -# pragma intrinsic(_BitScanForward64) -# pragma intrinsic(_BitScanReverse64) -# endif -# endif +#if !defined(__clang__) +#pragma managed(push, off) +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#if defined(_WIN64) +#pragma intrinsic(_BitScanForward64) +#pragma intrinsic(_BitScanReverse64) +#endif +#endif inline auto clz(uint32_t x) -> int { unsigned long r = 0; @@ -194,23 +195,23 @@ inline auto clz(uint32_t x) -> int { FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } -# define FMT_BUILTIN_CLZ(n) detail::clz(n) +#define FMT_BUILTIN_CLZ(n) detail::clz(n) inline auto clzll(uint64_t x) -> int { unsigned long r = 0; -# ifdef _WIN64 +#ifdef _WIN64 _BitScanReverse64(&r, x); -# else +#else // Scan the high 32 bits. if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); -# endif +#endif FMT_ASSERT(x != 0, ""); FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } -# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) +#define FMT_BUILTIN_CLZLL(n) detail::clzll(n) inline auto ctz(uint32_t x) -> int { unsigned long r = 0; @@ -219,27 +220,27 @@ inline auto ctz(uint32_t x) -> int { FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return static_cast(r); } -# define FMT_BUILTIN_CTZ(n) detail::ctz(n) +#define FMT_BUILTIN_CTZ(n) detail::ctz(n) inline auto ctzll(uint64_t x) -> int { unsigned long r = 0; FMT_ASSERT(x != 0, ""); FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -# ifdef _WIN64 +#ifdef _WIN64 _BitScanForward64(&r, x); -# else +#else // Scan the low 32 bits. if (_BitScanForward(&r, static_cast(x))) return static_cast(r); // Scan the high 32 bits. _BitScanForward(&r, static_cast(x >> 32)); r += 32; -# endif +#endif return static_cast(r); } -# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) -# if !defined(__clang__) -# pragma managed(pop) -# endif +#define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +#if !defined(__clang__) +#pragma managed(pop) +#endif } // namespace detail FMT_END_NAMESPACE #endif @@ -247,11 +248,10 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { -#if __cplusplus >= 202002L || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) -# define FMT_CONSTEXPR20 constexpr +#if __cplusplus >= 202002L || (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) +#define FMT_CONSTEXPR20 constexpr #else -# define FMT_CONSTEXPR20 +#define FMT_CONSTEXPR20 #endif // An equivalent of `*reinterpret_cast(&source)` that doesn't have @@ -288,30 +288,34 @@ struct fallback_uintptr { }; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline auto to_uintptr(const void* p) -> uintptr_t { - return bit_cast(p); -} +inline auto to_uintptr(const void* p) -> uintptr_t { return bit_cast(p); } #else using uintptr_t = fallback_uintptr; -inline auto to_uintptr(const void* p) -> fallback_uintptr { - return fallback_uintptr(p); -} +inline auto to_uintptr(const void* p) -> fallback_uintptr { return fallback_uintptr(p); } #endif // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. -template constexpr auto max_value() -> T { +template +constexpr auto max_value() -> T { return (std::numeric_limits::max)(); } -template constexpr auto num_bits() -> int { +template +constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { - return static_cast(sizeof(void*) * - std::numeric_limits::digits); +template <> +constexpr auto num_bits() -> int { + return 128; +} +template <> +constexpr auto num_bits() -> int { + return 128; +} +template <> +constexpr auto num_bits() -> int { + return static_cast(sizeof(void*) * std::numeric_limits::digits); } FMT_INLINE void assume(bool condition) { @@ -324,7 +328,8 @@ FMT_INLINE void assume(bool condition) { // An approximation of iterator_t for pre-C++20 systems. template using iterator_t = decltype(std::begin(std::declval())); -template using sentinel_t = decltype(std::end(std::declval())); +template +using sentinel_t = decltype(std::end(std::declval())); // A workaround for std::string not having mutable data() until C++17. template @@ -338,13 +343,19 @@ inline auto get_data(Container& c) -> typename Container::value_type* { #if defined(_SECURE_SCL) && _SECURE_SCL // Make a checked iterator to avoid MSVC warnings. -template using checked_ptr = stdext::checked_array_iterator; -template auto make_checked(T* p, size_t size) -> checked_ptr { +template +using checked_ptr = stdext::checked_array_iterator; +template +auto make_checked(T* p, size_t size) -> checked_ptr { return {p, size}; } #else -template using checked_ptr = T*; -template inline auto make_checked(T* p, size_t) -> T* { return p; } +template +using checked_ptr = T*; +template +inline auto make_checked(T* p, size_t) -> T* { + return p; +} #endif // Attempts to reserve space for n extra characters in the output range. @@ -382,7 +393,8 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template auto to_pointer(buffer_appender it, size_t n) -> T* { +template +auto to_pointer(buffer_appender it, size_t n) -> T* { buffer& buf = get_container(it); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; @@ -405,8 +417,7 @@ constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template -FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) - -> OutputIt { +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) -> OutputIt { for (Size i = 0; i < count; ++i) *out++ = value; return out; } @@ -448,8 +459,7 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, * occurs, this pointer will be a guess that depends on the particular * error, but it will always advance at least one byte. */ -FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) - -> const char* { +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; constexpr const int shiftc[] = {0, 18, 12, 6, 0}; @@ -517,25 +527,24 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { size_t* count; FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { *count += detail::to_unsigned( - 1 + - (error == 0 && cp >= 0x1100 && - (cp <= 0x115f || // Hangul Jamo init. consonants - cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET〈 - cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET 〉 - // CJK ... Yi except Unicode Character “〿”: - (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || - (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables - (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs - (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms - (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms - (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms - (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms - (cp >= 0x20000 && cp <= 0x2fffd) || // CJK - (cp >= 0x30000 && cp <= 0x3fffd) || - // Miscellaneous Symbols and Pictographs + Emoticons: - (cp >= 0x1f300 && cp <= 0x1f64f) || - // Supplemental Symbols and Pictographs: - (cp >= 0x1f900 && cp <= 0x1f9ff)))); + 1 + (error == 0 && cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET〈 + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET 〉 + // CJK ... Yi except Unicode Character “〿”: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); } }; for_each_codepoint(s, count_code_points{&num_code_points}); @@ -543,8 +552,8 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { } inline auto compute_width(basic_string_view s) -> size_t { - return compute_width(basic_string_view( - reinterpret_cast(s.data()), s.size())); + return compute_width( + basic_string_view(reinterpret_cast(s.data()), s.size())); } template @@ -554,8 +563,7 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { } // Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { const char8_type* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { @@ -565,11 +573,11 @@ inline auto code_point_index(basic_string_view s, size_t n) } template -using is_fast_float = bool_constant::is_iec559 && - sizeof(T) <= sizeof(double)>; +using is_fast_float = + bool_constant::is_iec559 && sizeof(T) <= sizeof(double)>; #ifndef FMT_USE_FULL_CACHE_DRAGONBOX -# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif template @@ -641,8 +649,7 @@ class basic_memory_buffer final : public detail::buffer { using value_type = T; using const_reference = const T&; - explicit basic_memory_buffer(const Allocator& alloc = Allocator()) - : alloc_(alloc) { + explicit basic_memory_buffer(const Allocator& alloc = Allocator()) : alloc_(alloc) { this->set(store_, SIZE); } ~basic_memory_buffer() { deallocate(); } @@ -680,8 +687,7 @@ class basic_memory_buffer final : public detail::buffer { Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT - -> basic_memory_buffer& { + auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -721,8 +727,7 @@ void basic_memory_buffer::grow(size_t size) { else if (new_capacity > max_size) new_capacity = size > max_size ? size : max_size; T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); + T* new_data = std::allocator_traits::allocate(alloc_, new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(old_data, old_data + this->size(), detail::make_checked(new_data, new_capacity)); @@ -736,8 +741,7 @@ void basic_memory_buffer::grow(size_t size) { using memory_buffer = basic_memory_buffer; template -struct is_contiguous> : std::true_type { -}; +struct is_contiguous> : std::true_type {}; namespace detail { FMT_API void print(std::FILE*, string_view); @@ -748,8 +752,7 @@ FMT_CLASS_API class FMT_API format_error : public std::runtime_error { public: explicit format_error(const char* message) : std::runtime_error(message) {} - explicit format_error(const std::string& message) - : std::runtime_error(message) {} + explicit format_error(const std::string& message) : std::runtime_error(message) {} format_error(const format_error&) = default; format_error& operator=(const format_error&) = default; format_error(format_error&&) = default; @@ -766,13 +769,11 @@ class FMT_API format_error : public std::runtime_error { \endrst */ template > -FMT_INLINE auto make_args_checked(const S& fmt, - const remove_reference_t&... args) +FMT_INLINE auto make_args_checked(const S& fmt, const remove_reference_t&... args) -> format_arg_store, remove_reference_t...> { static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, + detail::count<(std::is_base_of>::value && + std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); detail::check_format_string(fmt); return {args...}; @@ -781,10 +782,11 @@ FMT_INLINE auto make_args_checked(const S& fmt, // compile-time support namespace detail_exported { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template struct fixed_string { +template +struct fixed_string { constexpr fixed_string(const Char (&str)[N]) { - detail::copy_str(static_cast(str), - str + N, data); + detail::copy_str(static_cast(str), str + N, + data); } Char data[N]{}; }; @@ -792,8 +794,7 @@ template struct fixed_string { // Converts a compile-time string to basic_string_view. template -constexpr auto compile_string_to_view(const Char (&s)[N]) - -> basic_string_view { +constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view { // Remove trailing NUL character if needed. Won't be present if this is used // with a raw character array (i.e. not defined as a string). return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; @@ -807,18 +808,18 @@ constexpr auto compile_string_to_view(detail::std_string_view s) FMT_BEGIN_DETAIL_NAMESPACE -inline void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); -} +inline void throw_format_error(const char* message) { FMT_THROW(format_error(message)); } -template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; -template <> struct is_integral : std::true_type {}; +template +struct is_integral : std::is_integral {}; +template <> +struct is_integral : std::true_type {}; +template <> +struct is_integral : std::true_type {}; template -using is_signed = - std::integral_constant::is_signed || - std::is_same::value>; +using is_signed = std::integral_constant::is_signed || + std::is_same::value>; // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. @@ -842,41 +843,38 @@ FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { // represent all values of an integral type T. template using uint32_or_64_or_128_t = - conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, - uint32_t, + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, conditional_t() <= 64, uint64_t, uint128_t>>; template using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 + (factor)*1000000, (factor)*10000000, (factor)*100000000, (factor)*1000000000 // Static data is placed in this class template for the header-only config. -template struct basic_data { +template +struct basic_data { // log10(2) = 0x0.4d104d427de7fbcc... static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; // GCC generates slightly better code for pairs than chars. FMT_API static constexpr const char digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, + {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, + {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'}, {'2', '0'}, + {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, + {'2', '8'}, {'2', '9'}, {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, + {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, + {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, + {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, {'6', '0'}, {'6', '1'}, {'6', '2'}, + {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, + {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, + {'7', '7'}, {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, {'9', '0'}, + {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, + {'9', '8'}, {'9', '9'}}; FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; @@ -894,7 +892,8 @@ extern template struct basic_data; // This is a struct rather than an alias to avoid shadowing warnings in gcc. struct data : basic_data<> {}; -template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { +template +FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead @@ -921,15 +920,15 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { if (!is_constant_evaluated()) { // https://github.com/fmtlib/format-benchmark/blob/master/digits10 // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). - constexpr uint16_t bsr2log10[] = { - 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + constexpr uint16_t bsr2log10[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, + 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - constexpr const uint64_t zero_or_powers_of_10[] = { - 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; + constexpr const uint64_t zero_or_powers_of_10[] = {0, 0, FMT_POWERS_OF_10(1U), + FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); } #endif @@ -950,7 +949,8 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int { return num_digits; } -template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; +template <> +auto count_digits<4>(detail::fallback_uintptr n) -> int; // It is a separate function rather than a part of count_digits to workaround // the lack of static constexpr in constexpr functions. @@ -985,17 +985,21 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { return count_digits_fallback(n); } -template constexpr auto digits10() FMT_NOEXCEPT -> int { +template +constexpr auto digits10() FMT_NOEXCEPT -> int { return std::numeric_limits::digits10; } -template <> constexpr auto digits10() FMT_NOEXCEPT -> int { +template <> +constexpr auto digits10() FMT_NOEXCEPT -> int { return 38; } -template <> constexpr auto digits10() FMT_NOEXCEPT -> int { +template <> +constexpr auto digits10() FMT_NOEXCEPT -> int { return 38; } -template struct thousands_sep_result { +template +struct thousands_sep_result { std::string grouping; Char thousands_sep; }; @@ -1014,15 +1018,18 @@ inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { template FMT_API auto decimal_point_impl(locale_ref loc) -> Char; -template inline auto decimal_point(locale_ref loc) -> Char { +template +inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } -template <> inline auto decimal_point(locale_ref loc) -> wchar_t { +template <> +inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } // Compares two characters for equality. -template auto equal2(const Char* lhs, const char* rhs) -> bool { +template +auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == rhs[0] && lhs[1] == rhs[1]; } inline auto equal2(const char* lhs, const char* rhs) -> bool { @@ -1030,13 +1037,15 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { } // Copies two characters from src to dst. -template void copy2(Char* dst, const char* src) { +template +void copy2(Char* dst, const char* src) { *dst++ = static_cast(*src++); *dst = static_cast(*src); } FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } -template struct format_decimal_result { +template +struct format_decimal_result { Iterator begin; Iterator end; }; @@ -1093,15 +1102,15 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, do { const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); + *--buffer = + static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= BASE_BITS) != 0); return end; } template -auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - bool = false) -> Char* { +auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, bool = false) + -> Char* { auto char_digits = std::numeric_limits::digits / 4; int start = (num_digits + char_digits - 1) / char_digits - 1; if (int start_digits = num_digits % char_digits) { @@ -1122,8 +1131,7 @@ auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, } template -inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) - -> It { +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -> It { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_uint(ptr, value, num_digits, upper); return out; @@ -1150,9 +1158,11 @@ class utf8_to_utf16 { namespace dragonbox { // Type-specific information that Dragonbox uses. -template struct float_info; +template +struct float_info; -template <> struct float_info { +template <> +struct float_info { using carrier_uint = uint32_t; static const int significand_bits = 23; static const int exponent_bits = 8; @@ -1178,7 +1188,8 @@ template <> struct float_info { static const int max_trailing_zeros = 7; }; -template <> struct float_info { +template <> +struct float_info { using carrier_uint = uint64_t; static const int significand_bits = 52; static const int exponent_bits = 11; @@ -1204,7 +1215,8 @@ template <> struct float_info { static const int max_trailing_zeros = 16; }; -template struct decimal_fp { +template +struct decimal_fp { using significand_type = typename float_info::carrier_uint; significand_type significand; int exponent; @@ -1215,8 +1227,7 @@ FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; } // namespace dragonbox template -constexpr auto exponent_mask() -> - typename dragonbox::float_info::carrier_uint { +constexpr auto exponent_mask() -> typename dragonbox::float_info::carrier_uint { using uint = typename dragonbox::float_info::carrier_uint; return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) << dragonbox::float_info::significand_bits; @@ -1245,43 +1256,39 @@ auto write_exponent(int exp, It it) -> It { } template -auto format_float(T value, int precision, float_specs specs, buffer& buf) - -> int; +auto format_float(T value, int precision, float_specs specs, buffer& buf) -> int; // Formats a floating-point number with snprintf. template -auto snprintf_float(T value, int precision, float_specs specs, - buffer& buf) -> int; +auto snprintf_float(T value, int precision, float_specs specs, buffer& buf) -> int; -template auto promote_float(T value) -> T { return value; } -inline auto promote_float(float value) -> double { - return static_cast(value); +template +auto promote_float(T value) -> T { + return value; } +inline auto promote_float(float value) -> double { return static_cast(value); } template -FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, - const fill_t& fill) -> OutputIt { +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t& fill) + -> OutputIt { auto fill_size = fill.size(); if (fill_size == 1) return detail::fill_n(it, n, fill[0]); auto data = fill.data(); - for (size_t i = 0; i < n; ++i) - it = copy_str(data, data + fill_size, it); + for (size_t i = 0; i < n; ++i) it = copy_str(data, data + fill_size, it); return it; } // Writes the output of f, padded according to format specifications in specs. // size: output size in code units. // width: output display width in (terminal) column positions. -template -FMT_CONSTEXPR auto write_padded(OutputIt out, - const basic_format_specs& specs, +template +FMT_CONSTEXPR auto write_padded(OutputIt out, const basic_format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; - auto* shifts = align == align::left ? data::left_padding_shifts - : data::right_padding_shifts; + auto* shifts = + align == align::left ? data::left_padding_shifts : data::right_padding_shifts; size_t left_padding = padding >> shifts[specs.align]; size_t right_padding = padding - left_padding; auto it = reserve(out, size + padding * specs.fill.size()); @@ -1291,8 +1298,7 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, return base_iterator(out, it); } -template +template constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); @@ -1300,18 +1306,17 @@ constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, - const basic_format_specs& specs) - -> OutputIt { - return write_padded( - out, specs, bytes.size(), [bytes](reserve_iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); - }); + const basic_format_specs& specs) -> OutputIt { + return write_padded(out, specs, bytes.size(), + [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); } template -auto write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs* specs) -> OutputIt { +auto write_ptr(OutputIt out, UIntPtr value, const basic_format_specs* specs) + -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { @@ -1325,25 +1330,23 @@ auto write_ptr(OutputIt out, UIntPtr value, template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, - const basic_format_specs& specs) - -> OutputIt { + const basic_format_specs& specs) -> OutputIt { return write_padded(out, specs, 1, [=](reserve_iterator it) { *it++ = value; return it; }); } template -FMT_CONSTEXPR auto write(OutputIt out, Char value, - const basic_format_specs& specs, +FMT_CONSTEXPR auto write(OutputIt out, Char value, const basic_format_specs& specs, locale_ref loc = {}) -> OutputIt { - return check_char_specs(specs) - ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); + return check_char_specs(specs) ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); } // Data for write_int that doesn't depend on output iterator type. It is used to // avoid template code bloat. -template struct write_int_data { +template +struct write_int_data { size_t size; size_t padding; @@ -1368,8 +1371,7 @@ template struct write_int_data { // where are written by write_digits(it). // prefix contains chars in three lower bytes and the size in the fourth byte. template -FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, - unsigned prefix, +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, unsigned prefix, const basic_format_specs& specs, W write_digits) -> OutputIt { // Slightly faster check for specs.width == 0 && specs.precision == -1. @@ -1393,8 +1395,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, template auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, locale_ref loc) - -> bool { + const basic_format_specs& specs, locale_ref loc) -> bool { static_assert(std::is_same, UInt>::value, ""); const auto sep_size = 1; auto ts = thousands_sep(loc); @@ -1423,24 +1424,22 @@ auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, auto p = buffer.data() + size - 1; for (int i = num_digits - 1; i > 0; --i) { *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) + if (*group <= 0 || ++digit_index % *group != 0 || *group == max_value()) continue; if (group + 1 != groups.cend()) { digit_index = 0; ++group; } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); + std::uninitialized_copy(s.data(), s.data() + s.size(), make_checked(p, s.size())); p -= s.size(); } *p-- = static_cast(*digits); if (prefix != 0) *p = static_cast(prefix); auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, [=](reserve_iterator it) { - return copy_str(data, data + size, it); - }); + out = write_padded(out, specs, usize, usize, + [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); return true; } @@ -1449,7 +1448,8 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { prefix += (1u + (value > 0xff ? 1 : 0)) << 24; } -template struct write_int_arg { +template +struct write_int_arg { UInt abs_value; unsigned prefix; }; @@ -1477,74 +1477,71 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, auto prefix = arg.prefix; auto utype = static_cast(specs.type); switch (specs.type) { - case 0: - case 'd': { - if (specs.localized && - write_int_localized(out, static_cast>(abs_value), - prefix, specs, loc)) { - return out; + case 0: + case 'd': { + if (specs.localized && + write_int_localized(out, static_cast>(abs_value), prefix, + specs, loc)) { + return out; + } + auto num_digits = count_digits(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); } - auto num_digits = count_digits(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - case 'x': - case 'X': { - if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); - bool upper = specs.type != 'x'; - int num_digits = count_digits<4>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, upper); - }); - } - case 'b': - case 'B': { - if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); - int num_digits = count_digits<1>(abs_value); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } - case 'o': { - int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix_append(prefix, '0'); + case 'x': + case 'X': { + if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); + bool upper = specs.type != 'x'; + int num_digits = count_digits<4>(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, upper); + }); } - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); - } - case 'c': - return write_char(out, static_cast(abs_value), specs); - default: - FMT_THROW(format_error("invalid type specifier")); + case 'b': + case 'B': { + if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); + int num_digits = count_digits<1>(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + case 'o': { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix_append(prefix, '0'); + } + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + case 'c': + return write_char(out, static_cast(abs_value), specs); + default: + FMT_THROW(format_error("invalid type specifier")); } return out; } template ::value && - !std::is_same::value && + FMT_ENABLE_IF(is_integral::value && !std::is_same::value && std::is_same>::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value, - const basic_format_specs& specs, locale_ref loc) - -> OutputIt { +FMT_CONSTEXPR auto write(OutputIt out, T value, const basic_format_specs& specs, + locale_ref loc) -> OutputIt { return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } // An inlined version of write used in format string compilation. template ::value && - !std::is_same::value && + FMT_ENABLE_IF(is_integral::value && !std::is_same::value && !std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const basic_format_specs& specs, - locale_ref loc) -> OutputIt { + const basic_format_specs& specs, locale_ref loc) + -> OutputIt { return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } @@ -1555,24 +1552,19 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - auto width = - specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; - return write_padded(out, specs, size, width, - [=](reserve_iterator it) { - return copy_str(data, data + size, it); - }); + auto width = specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; + return write_padded(out, specs, size, width, [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); } template -FMT_CONSTEXPR auto write(OutputIt out, - basic_string_view> s, - const basic_format_specs& specs, locale_ref) - -> OutputIt { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view> s, + const basic_format_specs& specs, locale_ref) -> OutputIt { return write(out, s, specs); } template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, - const basic_format_specs& specs, locale_ref) - -> OutputIt { + const basic_format_specs& specs, locale_ref) -> OutputIt { return check_cstring_type_spec(specs.type) ? write(out, basic_string_view(s), specs, {}) : write_ptr(out, to_uintptr(s), &specs); @@ -1581,8 +1573,7 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, template auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, const float_specs& fspecs) -> OutputIt { - auto str = - isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + auto str = isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); @@ -1617,17 +1608,15 @@ inline auto write_significand(OutputIt out, const char* significand, return copy_str(significand, significand + significand_size, out); } template -inline auto write_significand(OutputIt out, UInt significand, - int significand_size) -> OutputIt { +inline auto write_significand(OutputIt out, UInt significand, int significand_size) + -> OutputIt { return format_decimal(out, significand, significand_size).end; } -template ::value)> +template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; + if (!decimal_point) return format_decimal(out, significand, significand_size).end; auto end = format_decimal(out + 1, significand, significand_size).end; if (integral_size == 1) { out[0] = out[1]; @@ -1641,22 +1630,19 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, template >::value)> -inline auto write_significand(OutputIt out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) -> OutputIt { +inline auto write_significand(OutputIt out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> OutputIt { // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. Char buffer[digits10() + 2]; - auto end = write_significand(buffer, significand, significand_size, - integral_size, decimal_point); + auto end = write_significand(buffer, significand, significand_size, integral_size, + decimal_point); return detail::copy_str_noinline(buffer, end, out); } template -inline auto write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) -> OutputIt { - out = detail::copy_str_noinline(significand, - significand + integral_size, out); +inline auto write_significand(OutputIt out, const char* significand, int significand_size, + int integral_size, Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline(significand, significand + integral_size, out); if (!decimal_point) return out; *out++ = decimal_point; return detail::copy_str_noinline(significand + integral_size, @@ -1664,9 +1650,8 @@ inline auto write_significand(OutputIt out, const char* significand, } template -auto write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) -> OutputIt { +auto write_float(OutputIt out, const DecimalFP& fp, const basic_format_specs& specs, + float_specs fspecs, Char decimal_point) -> OutputIt { auto significand = fp.significand; int significand_size = get_significand_size(fp); static const Char zero = static_cast('0'); @@ -1702,8 +1687,7 @@ auto write_float(OutputIt out, const DecimalFP& fp, auto write = [=](iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); // Insert a decimal point after the first digit and add an exponent. - it = write_significand(it, significand, significand_size, 1, - decimal_point); + it = write_significand(it, significand, significand_size, 1, decimal_point); if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); *it++ = static_cast(exp_char); return write_exponent(output_exp, it); @@ -1739,15 +1723,13 @@ auto write_float(OutputIt out, const DecimalFP& fp, size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); - it = write_significand(it, significand, significand_size, exp, - decimal_point); + it = write_significand(it, significand, significand_size, exp, decimal_point); return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } // 1234e-6 -> 0.001234 int num_zeros = -exp; - if (significand_size == 0 && fspecs.precision >= 0 && - fspecs.precision < num_zeros) { + if (significand_size == 0 && fspecs.precision >= 0 && fspecs.precision < num_zeros) { num_zeros = fspecs.precision; } bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; @@ -1764,8 +1746,8 @@ auto write_float(OutputIt out, const DecimalFP& fp, template ::value)> -auto write(OutputIt out, T value, basic_format_specs specs, - locale_ref loc = {}) -> OutputIt { +auto write(OutputIt out, T value, basic_format_specs specs, locale_ref loc = {}) + -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; @@ -1791,8 +1773,7 @@ auto write(OutputIt out, T value, basic_format_specs specs, if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); snprintf_float(promote_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, - specs); + return write_bytes(out, {buffer.data(), buffer.size()}, specs); } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; if (fspecs.format == float_format::exp) { @@ -1805,8 +1786,7 @@ auto write(OutputIt out, T value, basic_format_specs specs, fspecs.use_grisu = is_fast_float(); int exp = format_float(promote_float(value), precision, fspecs, buffer); fspecs.precision = precision; - Char point = - fspecs.locale ? decimal_point(loc) : static_cast('.'); + Char point = fspecs.locale ? decimal_point(loc) : static_cast('.'); auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; return write_float(out, fp, specs, fspecs, point); } @@ -1837,22 +1817,20 @@ auto write(OutputIt out, T value) -> OutputIt { } template ::value && - !is_fast_float::value)> + FMT_ENABLE_IF(std::is_floating_point::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs()); } template -auto write(OutputIt out, monostate, basic_format_specs = {}, - locale_ref = {}) -> OutputIt { +auto write(OutputIt out, monostate, basic_format_specs = {}, locale_ref = {}) + -> OutputIt { FMT_ASSERT(false, ""); return out; } template -FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) - -> OutputIt { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { auto it = reserve(out, value.size()); it = copy_str_noinline(value.begin(), value.end(), it); return base_iterator(out, it); @@ -1865,8 +1843,7 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { } template ::value && - !std::is_same::value && + FMT_ENABLE_IF(is_integral::value && !std::is_same::value && !std::is_same::value)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { auto abs_value = static_cast>(value); @@ -1889,21 +1866,19 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { // FMT_ENABLE_IF() condition separated to workaround MSVC bug template < typename Char, typename OutputIt, typename T, - bool check = - std::is_enum::value && !std::is_same::value && - mapped_type_constant>::value != - type::custom_type, + bool check = std::is_enum::value && !std::is_same::value && + mapped_type_constant>::value != + type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - return write( - out, static_cast::type>(value)); + return write(out, static_cast::type>(value)); } template ::value)> FMT_CONSTEXPR auto write(OutputIt out, T value, - const basic_format_specs& specs = {}, - locale_ref = {}) -> OutputIt { + const basic_format_specs& specs = {}, locale_ref = {}) + -> OutputIt { return specs.type && specs.type != 's' ? write(out, value ? 1 : 0, specs, {}) : write_bytes(out, value ? "true" : "false", specs); @@ -1917,8 +1892,7 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { } template -FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) - -> OutputIt { +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -> OutputIt { if (!value) { FMT_THROW(format_error("string pointer is null")); } else { @@ -1930,31 +1904,29 @@ FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) template ::value)> -auto write(OutputIt out, const T* value, - const basic_format_specs& specs = {}, locale_ref = {}) - -> OutputIt { +auto write(OutputIt out, const T* value, const basic_format_specs& specs = {}, + locale_ref = {}) -> OutputIt { check_pointer_type_spec(specs.type, error_handler()); return write_ptr(out, to_uintptr(value), &specs); } template -FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> - typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { using context_type = basic_format_context; - using formatter_type = - conditional_t::value, - typename context_type::template formatter_type, - fallback_formatter>; + using formatter_type = conditional_t::value, + typename context_type::template formatter_type, + fallback_formatter>; context_type ctx(out, {}, {}); return formatter_type().format(value, ctx); } // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. -template struct default_arg_formatter { +template +struct default_arg_formatter { using iterator = buffer_appender; using context = buffer_context; @@ -1962,7 +1934,8 @@ template struct default_arg_formatter { basic_format_args args; locale_ref loc; - template auto operator()(T value) -> iterator { + template + auto operator()(T value) -> iterator { return write(out, value); } auto operator()(typename basic_format_arg::handle h) -> iterator { @@ -1973,7 +1946,8 @@ template struct default_arg_formatter { } }; -template struct arg_formatter { +template +struct arg_formatter { using iterator = buffer_appender; using context = buffer_context; @@ -1992,24 +1966,25 @@ template struct arg_formatter { } }; -template struct custom_formatter { +template +struct custom_formatter { basic_format_parse_context& parse_ctx; buffer_context& ctx; - void operator()( - typename basic_format_arg>::handle h) const { + void operator()(typename basic_format_arg>::handle h) const { h.format(parse_ctx, ctx); } - template void operator()(T) const {} + template + void operator()(T) const {} }; template using is_integer = bool_constant::value && !std::is_same::value && - !std::is_same::value && - !std::is_same::value>; + !std::is_same::value && !std::is_same::value>; -template class width_checker { +template +class width_checker { public: explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} @@ -2029,7 +2004,8 @@ template class width_checker { ErrorHandler& handler_; }; -template class precision_checker { +template +class precision_checker { public: explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} @@ -2049,8 +2025,7 @@ template class precision_checker { ErrorHandler& handler_; }; -template