diff --git a/DESCRIPTION b/DESCRIPTION index 9e2f8259..283631f7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: cpp11 Title: A C++11 Interface for R's C Interface -Version: 0.5.2.9000 +Version: 0.6.0.9000 Authors@R: c( person("Davis", "Vaughan", email = "davis@posit.co", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-4777-038X")), @@ -31,7 +31,6 @@ Suggests: glue, knitr, lobstr, - mockery, progress, rmarkdown, scales, diff --git a/NAMESPACE b/NAMESPACE index 876655dc..d9bcf668 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,4 +4,5 @@ export(cpp_eval) export(cpp_function) export(cpp_register) export(cpp_source) +export(cpp_unvendor) export(cpp_vendor) diff --git a/NEWS.md b/NEWS.md index f9a44056..bb23cc66 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,12 @@ * Fixed an issue related to `-Wdeprecated-literal-operator` (#447, @andrjohns). +# cpp11 0.5.1.9000 + +* Uses testthat 3e style tests (#402). +* Removes the mockery dependence. +* The vendoring function accepts a custom path (i.e., to use the GitHub version of the package) + # cpp11 0.5.1 * cpp11 now requires R >=4.0.0, in line with the diff --git a/R/register.R b/R/register.R index 3ea10d52..5022e801 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,35 +214,75 @@ 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 <- "" } - 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})") ) - out <- glue::glue_data(funs, ' - {name} <- function({list_params}) {{ - {calls} - }} - ') + # Parse and associate Roxygen comments + funs$roxygen_comment <- mapply(function(file, line) { + 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 { + "" + } + }, 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 {calls}\n}}") + } + }, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, 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(list()) + } + + roxygen_comments <- mapply(function(start, end) { + roxygen_lines <- lines[(start + 1):(end - 1)] + roxygen_lines <- sub("^", "#' ", roxygen_lines) + list(line = end, text = paste(roxygen_lines, collapse = "\n")) + }, roxygen_start, roxygen_end, SIMPLIFY = FALSE) + + roxygen_comments +} + 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/R/source.R b/R/source.R index fc9124d5..3927cc6e 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()) @@ -163,12 +170,15 @@ generate_cpp_name <- function(name, loaded_dlls = c("cpp11", names(getLoadedDLLs sprintf("%s.%s", new_name, ext) } - - -generate_include_paths <- function(packages) { +generate_include_paths <- function(packages, custom_path = NULL) { out <- character(length(packages)) for (i in seq_along(packages)) { - path <- system.file(package = packages[[i]], "include") + if (!is.null(custom_path)) { + path <- custom_path + } else { + path <- system.file(package = packages[[i]], "include") + } + if (is_windows()) { path <- utils::shortPathName(path) } @@ -183,7 +193,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 +203,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 +212,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 +225,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/R/unvendor.R b/R/unvendor.R new file mode 100644 index 00000000..186bf696 --- /dev/null +++ b/R/unvendor.R @@ -0,0 +1,39 @@ +#' Unvendor the cpp11 dependency +#' +#' This function removes the vendored cpp11 headers from your package and +#' restores the `LinkingTo: cpp11` field in the DESCRIPTION file if it was removed. +#' +#' @inheritParams cpp_register +#' @return The file path to the unvendored code (invisibly). +#' @export +#' @examples +#' # create a new directory +#' dir <- tempfile() +#' dir.create(dir) +#' +#' # vendor the cpp11 headers into the directory +#' cpp_vendor(dir) +#' +#' # unvendor the cpp11 headers from the directory +#' cpp_unvendor(dir) +#' +#' list.files(file.path(dir, "inst", "include", "cpp11")) +#' +#' # cleanup +#' unlink(dir, recursive = TRUE) +cpp_unvendor <- function(path = ".") { + new <- file.path(path, "inst", "include", "cpp11") + + if (!dir.exists(new)) { + stop("'", new, "' does not exist", call. = FALSE) + } + + unlink(new, recursive = TRUE) + + cpp11_hpp <- file.path(dirname(new), "cpp11.hpp") + if (file.exists(cpp11_hpp)) { + unlink(cpp11_hpp) + } + + invisible(new) +} diff --git a/R/vendor.R b/R/vendor.R index 5fe10fe2..fb90bd31 100644 --- a/R/vendor.R +++ b/R/vendor.R @@ -8,14 +8,17 @@ #' 'cpp11 version: XYZ' to the top of the files, where XYZ is the version of #' cpp11 currently installed on your machine. #' -#' If you choose to vendor the headers you should _remove_ `LinkingTo: -#' cpp11` from your DESCRIPTION. -#' #' **Note**: vendoring places the responsibility of updating the code on #' **you**. Bugfixes and new features in cpp11 will not be available for your #' code until you run `cpp_vendor()` again. #' #' @inheritParams cpp_register +#' @param dir The directoyy to vendor the code into. +#' @param subdir The subdirectory to vendor the code into. +#' @param headers The path to the cpp11 headers to vendor. By default this is +#' the path where R installed the cpp11 package. You can change this to +#' use a different version of cpp11, such as as the development version +#' from GitHub. #' @return The file path to the vendored code (invisibly). #' @export #' @examples @@ -30,34 +33,85 @@ #' #' # cleanup #' unlink(dir, recursive = TRUE) -cpp_vendor <- function(path = ".") { - new <- file.path(path, "inst", "include", "cpp11") +cpp_vendor <- function(dir = NULL, subdir = "/inst/include") { + if (is.null(dir)) { + stop("You must provide a path to vendor the code into", call. = FALSE) + } + + path <- paste0(dir, subdir) - if (dir.exists(new)) { - stop("'", new, "' already exists\n * run unlink('", new, "', recursive = TRUE)", call. = FALSE) + path2 <- file.path(path, "cpp11") + if (dir.exists(path2)) { + stop("'", path2, "' already exists\n * run unlink('", path2, "', recursive = TRUE)", call. = FALSE) } - dir.create(new , recursive = TRUE, showWarnings = FALSE) + # Vendor cpp11 ---- + + dir.create( + path2, + recursive = TRUE, + showWarnings = FALSE + ) + + current_cpp11 <- system.file( + "include", + "cpp11", + package = "cpp11" + ) - current <- system.file("include", "cpp11", package = "cpp11") - if (!nzchar(current)) { + if (!nzchar(current_cpp11)) { stop("cpp11 is not installed", call. = FALSE) } cpp11_version <- utils::packageVersion("cpp11") - cpp11_header <- sprintf("// cpp11 version: %s\n// vendored on: %s", cpp11_version, Sys.Date()) + cpp11_header <- sprintf( + "// cpp11 version: %s\n// vendored on: %s", + cpp11_version, + Sys.Date() + ) - files <- list.files(current, full.names = TRUE) + write_header( + path, "cpp11.hpp", "cpp11", + cpp11_header + ) + copy_files( + list.files(current_cpp11, full.names = TRUE), + path, "cpp11", cpp11_header + ) + + # Additional steps to make vendoring work ---- + + message(paste( + "Makevars and/or Makevars.win should have a line such as", + "'PKG_CPPFLAGS = -I../inst/include'" + )) + + message("DESCRIPTION should not have lines such as 'LinkingTo: cpp11'") + + files <- list.files(headers, full.names = TRUE) + + invisible(path) +} + +write_header <- function(path, header, pkg, cpp11armadillo_header) { writeLines( - c(cpp11_header, readLines(system.file("include", "cpp11.hpp", package = "cpp11"))), - file.path(dirname(new), "cpp11.hpp") + c( + cpp11armadillo_header, + readLines( + system.file("include", header, package = pkg) + ) + ), + file.path(path, header) ) +} +copy_files <- function(files, path, out, cpp11armadillo_header) { for (f in files) { - writeLines(c(cpp11_header, readLines(f)), file.path(new, basename(f))) + writeLines( + c(cpp11armadillo_header, readLines(f)), + file.path(path, out, basename(f)) + ) } - - invisible(new) } 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/_pkgdown.yml b/_pkgdown.yml index 06dec1ab..f38f387b 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -25,6 +25,7 @@ reference: - title: Vendoring cpp11 contents: - cpp_vendor + - cpp_unvendor navbar: type: default 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..256f29df 100644 --- a/cpp11test/NAMESPACE +++ b/cpp11test/NAMESPACE @@ -1,6 +1,12 @@ # 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) +importFrom(cpp11,cpp_source) useDynLib(cpp11test, .registration = TRUE) diff --git a/cpp11test/R/cpp11.R b/cpp11test/R/cpp11.R index 038e7b76..612ab85e 100644 --- a/cpp11test/R/cpp11.R +++ b/cpp11test/R/cpp11.R @@ -84,10 +84,26 @@ grow_ <- function(n) { .Call(`_cpp11test_grow_`, n) } +grow_cplx_ <- function(n) { + .Call(`_cpp11test_grow_cplx_`, n) +} + cpp11_insert_ <- function(num_sxp) { .Call(`_cpp11test_cpp11_insert_`, num_sxp) } +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) +} + gibbs_cpp <- function(N, thin) { .Call(`_cpp11test_gibbs_cpp`, N, thin) } @@ -112,6 +128,18 @@ col_sums <- function(x) { .Call(`_cpp11test_col_sums`, x) } +mat_mat_copy_dimnames <- function(x) { + .Call(`_cpp11test_mat_mat_copy_dimnames`, x) +} + +mat_sexp_copy_dimnames <- function(x) { + .Call(`_cpp11test_mat_sexp_copy_dimnames`, x) +} + +mat_mat_create_dimnames <- function() { + .Call(`_cpp11test_mat_mat_create_dimnames`) +} + protect_one_ <- function(x, n) { invisible(.Call(`_cpp11test_protect_one_`, x, n)) } @@ -156,6 +184,63 @@ rcpp_release_ <- function(n) { invisible(.Call(`_cpp11test_rcpp_release_`, n)) } +notroxcpp1_ <- function(x) { + .Call(`_cpp11test_notroxcpp1_`, x) +} + +#' @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) +roxcpp2_ <- function(x) { + .Call(`_cpp11test_roxcpp2_`, x) +} + +#' @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) +roxcpp3_ <- function(x) { + .Call(`_cpp11test_roxcpp3_`, x) +} + +#' @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) +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 +#' my_fun <- function(x) { +#' roxcpp7_(x) +#' } +#' @seealso \code{\link{roxcpp1_}} +roxcpp7_ <- function(x) { + .Call(`_cpp11test_roxcpp7_`, x) +} + cpp11_safe_ <- function(x_sxp) { .Call(`_cpp11test_cpp11_safe_`, x_sxp) } @@ -168,6 +253,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) } @@ -196,6 +301,42 @@ sum_dbl_accumulate2_ <- function(x_sxp) { .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) } +sum_cplx_for_ <- function(x) { + .Call(`_cpp11test_sum_cplx_for_`, x) +} + +sum_cplx_for_2_ <- function(x) { + .Call(`_cpp11test_sum_cplx_for_2_`, x) +} + +sum_cplx_for_3_ <- function(x_sxp) { + .Call(`_cpp11test_sum_cplx_for_3_`, x_sxp) +} + +sum_cplx_for_4_ <- function(x_sxp) { + .Call(`_cpp11test_sum_cplx_for_4_`, x_sxp) +} + +sum_cplx_for_5_ <- function(x_sxp) { + .Call(`_cpp11test_sum_cplx_for_5_`, x_sxp) +} + +sum_cplx_for_6_ <- function(x_sxp) { + .Call(`_cpp11test_sum_cplx_for_6_`, x_sxp) +} + +sum_cplx_foreach_ <- function(x) { + .Call(`_cpp11test_sum_cplx_foreach_`, x) +} + +sum_cplx_accumulate_ <- function(x) { + .Call(`_cpp11test_sum_cplx_accumulate_`, x) +} + +sum_cplx_for2_ <- function(x_sxp) { + .Call(`_cpp11test_sum_cplx_for2_`, x_sxp) +} + sum_int_for_ <- function(x) { .Call(`_cpp11test_sum_int_for_`, x) } @@ -236,6 +377,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/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/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/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/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..17466bf6 --- /dev/null +++ b/cpp11test/man/roxcpp7_.Rd @@ -0,0 +1,22 @@ +% 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{ +my_fun <- function(x) { + roxcpp7_(x) +} +} +\seealso{ +\code{\link{roxcpp1_}} +} diff --git a/cpp11test/src/cpp11.cpp b/cpp11test/src/cpp11.cpp index 421de637..c74319f6 100644 --- a/cpp11test/src/cpp11.cpp +++ b/cpp11test/src/cpp11.cpp @@ -166,6 +166,13 @@ extern "C" SEXP _cpp11test_grow_(SEXP n) { return cpp11::as_sexp(grow_(cpp11::as_cpp>(n))); END_CPP11 } +// grow.cpp +cpp11::writable::complexes grow_cplx_(R_xlen_t n); +extern "C" SEXP _cpp11test_grow_cplx_(SEXP n) { + BEGIN_CPP11 + return cpp11::as_sexp(grow_cplx_(cpp11::as_cpp>(n))); + END_CPP11 +} // insert.cpp SEXP cpp11_insert_(SEXP num_sxp); extern "C" SEXP _cpp11test_cpp11_insert_(SEXP num_sxp) { @@ -173,6 +180,27 @@ 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 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 + 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) { @@ -215,6 +243,27 @@ extern "C" SEXP _cpp11test_col_sums(SEXP x) { return cpp11::as_sexp(col_sums(cpp11::as_cpp>>(x))); END_CPP11 } +// matrix.cpp +cpp11::doubles_matrix<> mat_mat_copy_dimnames(cpp11::doubles_matrix<> x); +extern "C" SEXP _cpp11test_mat_mat_copy_dimnames(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(mat_mat_copy_dimnames(cpp11::as_cpp>>(x))); + END_CPP11 +} +// matrix.cpp +SEXP mat_sexp_copy_dimnames(cpp11::doubles_matrix<> x); +extern "C" SEXP _cpp11test_mat_sexp_copy_dimnames(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(mat_sexp_copy_dimnames(cpp11::as_cpp>>(x))); + END_CPP11 +} +// matrix.cpp +cpp11::doubles_matrix<> mat_mat_create_dimnames(); +extern "C" SEXP _cpp11test_mat_mat_create_dimnames() { + BEGIN_CPP11 + return cpp11::as_sexp(mat_mat_create_dimnames()); + END_CPP11 +} // protect.cpp void protect_one_(SEXP x, int n); extern "C" SEXP _cpp11test_protect_one_(SEXP x, SEXP n) { @@ -303,6 +352,55 @@ extern "C" SEXP _cpp11test_rcpp_release_(SEXP n) { return R_NilValue; END_CPP11 } +// roxygen1.cpp +double notroxcpp1_(double x); +extern "C" SEXP _cpp11test_notroxcpp1_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(notroxcpp1_(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 +} +// 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) { @@ -324,6 +422,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) { @@ -373,6 +506,69 @@ extern "C" SEXP _cpp11test_sum_dbl_accumulate2_(SEXP x_sxp) { return cpp11::as_sexp(sum_dbl_accumulate2_(cpp11::as_cpp>(x_sxp))); END_CPP11 } +// sum.cpp +cpp11::r_complex sum_cplx_for_(cpp11::complexes x); +extern "C" SEXP _cpp11test_sum_cplx_for_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_(cpp11::as_cpp>(x))); + END_CPP11 +} +// sum.cpp +cpp11::complexes sum_cplx_for_2_(cpp11::complexes x); +extern "C" SEXP _cpp11test_sum_cplx_for_2_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_2_(cpp11::as_cpp>(x))); + END_CPP11 +} +// sum.cpp +std::complex sum_cplx_for_3_(cpp11::complexes x_sxp); +extern "C" SEXP _cpp11test_sum_cplx_for_3_(SEXP x_sxp) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_3_(cpp11::as_cpp>(x_sxp))); + END_CPP11 +} +// sum.cpp +std::complex sum_cplx_for_4_(SEXP x_sxp); +extern "C" SEXP _cpp11test_sum_cplx_for_4_(SEXP x_sxp) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_4_(cpp11::as_cpp>(x_sxp))); + END_CPP11 +} +// sum.cpp +SEXP sum_cplx_for_5_(SEXP x_sxp); +extern "C" SEXP _cpp11test_sum_cplx_for_5_(SEXP x_sxp) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_5_(cpp11::as_cpp>(x_sxp))); + END_CPP11 +} +// sum.cpp +cpp11::complexes sum_cplx_for_6_(SEXP x_sxp); +extern "C" SEXP _cpp11test_sum_cplx_for_6_(SEXP x_sxp) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for_6_(cpp11::as_cpp>(x_sxp))); + END_CPP11 +} +// sum.cpp +std::complex sum_cplx_foreach_(cpp11::complexes x); +extern "C" SEXP _cpp11test_sum_cplx_foreach_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_foreach_(cpp11::as_cpp>(x))); + END_CPP11 +} +// sum.cpp +std::complex sum_cplx_accumulate_(cpp11::complexes x); +extern "C" SEXP _cpp11test_sum_cplx_accumulate_(SEXP x) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_accumulate_(cpp11::as_cpp>(x))); + END_CPP11 +} +// sum.cpp +std::complex sum_cplx_for2_(SEXP x_sxp); +extern "C" SEXP _cpp11test_sum_cplx_for2_(SEXP x_sxp) { + BEGIN_CPP11 + return cpp11::as_sexp(sum_cplx_for2_(cpp11::as_cpp>(x_sxp))); + END_CPP11 +} // sum_int.cpp double sum_int_for_(cpp11::integers x); extern "C" SEXP _cpp11test_sum_int_for_(SEXP x) { @@ -443,6 +639,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() { @@ -472,6 +682,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 +700,13 @@ 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_cplx_", (DL_FUNC) &_cpp11test_grow_cplx_, 1}, + {"_cpp11test_mat_mat_copy_dimnames", (DL_FUNC) &_cpp11test_mat_mat_copy_dimnames, 1}, + {"_cpp11test_mat_mat_create_dimnames", (DL_FUNC) &_cpp11test_mat_mat_create_dimnames, 0}, + {"_cpp11test_mat_sexp_copy_dimnames", (DL_FUNC) &_cpp11test_mat_sexp_copy_dimnames, 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}, @@ -500,6 +719,10 @@ 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_ordered_map_to_list_2_", (DL_FUNC) &_cpp11test_ordered_map_to_list_2_, 1}, + {"_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}, @@ -518,8 +741,22 @@ 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_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_cplx_accumulate_", (DL_FUNC) &_cpp11test_sum_cplx_accumulate_, 1}, + {"_cpp11test_sum_cplx_for2_", (DL_FUNC) &_cpp11test_sum_cplx_for2_, 1}, + {"_cpp11test_sum_cplx_for_", (DL_FUNC) &_cpp11test_sum_cplx_for_, 1}, + {"_cpp11test_sum_cplx_for_2_", (DL_FUNC) &_cpp11test_sum_cplx_for_2_, 1}, + {"_cpp11test_sum_cplx_for_3_", (DL_FUNC) &_cpp11test_sum_cplx_for_3_, 1}, + {"_cpp11test_sum_cplx_for_4_", (DL_FUNC) &_cpp11test_sum_cplx_for_4_, 1}, + {"_cpp11test_sum_cplx_for_5_", (DL_FUNC) &_cpp11test_sum_cplx_for_5_, 1}, + {"_cpp11test_sum_cplx_for_6_", (DL_FUNC) &_cpp11test_sum_cplx_for_6_, 1}, + {"_cpp11test_sum_cplx_foreach_", (DL_FUNC) &_cpp11test_sum_cplx_foreach_, 1}, {"_cpp11test_sum_dbl_accumulate2_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate2_, 1}, {"_cpp11test_sum_dbl_accumulate_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate_, 1}, {"_cpp11test_sum_dbl_for2_", (DL_FUNC) &_cpp11test_sum_dbl_for2_, 1}, @@ -533,6 +770,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/grow.cpp b/cpp11test/src/grow.cpp index eb3f620b..cd20b292 100644 --- a/cpp11test/src/grow.cpp +++ b/cpp11test/src/grow.cpp @@ -1,3 +1,4 @@ +#include "cpp11/complexes.hpp" #include "cpp11/doubles.hpp" [[cpp11::register]] cpp11::writable::doubles grow_(R_xlen_t n) { @@ -9,3 +10,14 @@ return x; } + +[[cpp11::register]] cpp11::writable::complexes grow_cplx_(R_xlen_t n) { + cpp11::writable::complexes x; + R_xlen_t i = 0; + while (i < n) { + x.push_back(std::complex(i, i)); + i++; + } + + return x; +} diff --git a/cpp11test/src/map.cpp b/cpp11test/src/map.cpp new file mode 100644 index 00000000..71b3402d --- /dev/null +++ b/cpp11test/src/map.cpp @@ -0,0 +1,29 @@ +#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 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(); + for (int i = 0; i < n; i++) { + counts[x[i]]++; + } + return cpp11::as_sexp(counts); +} diff --git a/cpp11test/src/matrix.cpp b/cpp11test/src/matrix.cpp index 10348945..a875d73d 100644 --- a/cpp11test/src/matrix.cpp +++ b/cpp11test/src/matrix.cpp @@ -1,6 +1,8 @@ #include "cpp11/matrix.hpp" #include "Rmath.h" #include "cpp11/doubles.hpp" +#include "cpp11/list.hpp" +#include "cpp11/strings.hpp" using namespace cpp11; [[cpp11::register]] SEXP gibbs_cpp(int N, int thin) { @@ -104,3 +106,47 @@ using namespace Rcpp; return sums; } + +[[cpp11::register]] cpp11::doubles_matrix<> mat_mat_copy_dimnames( + cpp11::doubles_matrix<> x) { + cpp11::writable::doubles_matrix<> out = x; + + // SEXP dimnames = x.attr("dimnames"); + // if (dimnames != R_NilValue) { + // Rf_setAttrib(out.data(), R_DimNamesSymbol, dimnames); + // } + + out.attr("dimnames") = x.attr("dimnames"); + + return out; +} + +[[cpp11::register]] SEXP mat_sexp_copy_dimnames(cpp11::doubles_matrix<> x) { + cpp11::writable::doubles_matrix<> out = x; + + // SEXP dimnames = x.attr("dimnames"); + // if (dimnames != R_NilValue) { + // Rf_setAttrib(out.data(), R_DimNamesSymbol, dimnames); + // } + + out.attr("dimnames") = x.attr("dimnames"); + + return out; +} + +[[cpp11::register]] cpp11::doubles_matrix<> mat_mat_create_dimnames() { + cpp11::writable::doubles_matrix<> out(2, 2); + + out(0, 0) = 1; + out(0, 1) = 2; + out(1, 0) = 3; + out(1, 1) = 4; + + cpp11::writable::list dimnames(2); + dimnames[0] = cpp11::strings({"a", "b"}); + dimnames[1] = cpp11::strings({"c", "d"}); + + out.attr("dimnames") = dimnames; + + return out; +} 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..7ede7a08 --- /dev/null +++ b/cpp11test/src/roxygen3.cpp @@ -0,0 +1,38 @@ +#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 +my_fun <- function(x) { + roxcpp7_(x) +} +@seealso \code{\link{roxcpp1_}} +roxygen end */ +[[cpp11::register]] double roxcpp7_(double x) { + double y = x + 7.0; + return y; +} 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; +} diff --git a/cpp11test/src/sum.cpp b/cpp11test/src/sum.cpp index e685c7d1..cb8060b7 100644 --- a/cpp11test/src/sum.cpp +++ b/cpp11test/src/sum.cpp @@ -1,4 +1,5 @@ #include +#include "cpp11/complexes.hpp" #include "cpp11/doubles.hpp" [[cpp11::register]] double sum_dbl_for_(cpp11::doubles x) { @@ -58,3 +59,111 @@ const cpp11::doubles x(x_sxp, false); return std::accumulate(x.cbegin(), x.cend(), 0.); } + +// Pacha: Functions for complex data type + +[[cpp11::register]] cpp11::r_complex sum_cplx_for_(cpp11::complexes x) { + std::complex sum = {0.0, 0.0}; + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + return cpp11::r_complex(sum.real(), sum.imag()); +} + +[[cpp11::register]] cpp11::complexes sum_cplx_for_2_(cpp11::complexes x) { + std::complex sum = {0.0, 0.0}; + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + cpp11::writable::complexes result(1); + // result[0] = cpp11::r_complex(sum.real(), sum.imag()); + result[0] = sum; + + return result; +} + +[[cpp11::register]] std::complex sum_cplx_for_3_(cpp11::complexes x_sxp) { + std::complex sum = {0.0, 0.0}; + const cpp11::complexes x(x_sxp, false); + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + return sum; +} + +[[cpp11::register]] std::complex sum_cplx_for_4_(SEXP x_sxp) { + std::complex sum = {0.0, 0.0}; + const cpp11::complexes x(x_sxp, false); + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + return sum; +} + +[[cpp11::register]] SEXP sum_cplx_for_5_(SEXP x_sxp) { + std::complex sum = {0.0, 0.0}; + const cpp11::complexes x(x_sxp, false); + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + return cpp11::as_sexp(sum); +} + +[[cpp11::register]] cpp11::complexes sum_cplx_for_6_(SEXP x_sxp) { + std::complex sum = {0.0, 0.0}; + const cpp11::complexes x(x_sxp, false); + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + // sum.real(sum.real() + x[i].real()); + // sum.imag(sum.imag() + x[i].imag()); + sum += x[i]; + } + + return cpp11::as_sexp(sum); +} + +[[cpp11::register]] std::complex sum_cplx_foreach_(cpp11::complexes x) { + std::complex sum = {0.0, 0.0}; + for (const auto&& val : x) { + // sum.real(sum.real() + val.real()); + // sum.imag(sum.imag() + val.imag()); + sum += val; + } + + return sum; +} + +[[cpp11::register]] std::complex sum_cplx_accumulate_(cpp11::complexes x) { + return std::accumulate(x.cbegin(), x.cend(), std::complex(0.0, 0.0)); +} + +[[cpp11::register]] std::complex sum_cplx_for2_(SEXP x_sxp) { + std::complex sum = {0.0, 0.0}; + const cpp11::complexes x(x_sxp); + R_xlen_t n = x.size(); + for (R_xlen_t i = 0; i < n; ++i) { + sum += x[i]; + } + + return sum; +} diff --git a/cpp11test/src/test-complex.cpp b/cpp11test/src/test-complex.cpp new file mode 100644 index 00000000..750002f0 --- /dev/null +++ b/cpp11test/src/test-complex.cpp @@ -0,0 +1,514 @@ +#include "cpp11/complexes.hpp" + +#include "cpp11/strings.hpp" + +#include + +context("complexes-C++") { + test_that("complexes::r_vector(SEXP)") { + cpp11::complexes x(Rf_allocVector(CPLXSXP, 2)); + expect_true(x.size() == 2); + + expect_error(cpp11::complexes(Rf_allocVector(INTSXP, 2))); + } + + test_that("complexes::r_vector::const_iterator()") { + cpp11::complexes x(Rf_allocVector(CPLXSXP, 100)); + COMPLEX(x)[0] = Rcomplex{1, 1}; + COMPLEX(x)[1] = Rcomplex{2, 2}; + COMPLEX(x)[2] = Rcomplex{3, 3}; + COMPLEX(x)[3] = Rcomplex{4, 4}; + COMPLEX(x)[4] = Rcomplex{5, 5}; + COMPLEX(x)[97] = Rcomplex{98, 98}; + COMPLEX(x)[98] = Rcomplex{99, 99}; + COMPLEX(x)[99] = Rcomplex{100, 100}; + expect_true(x.size() == 100); + + auto it = x.begin(); + auto it2 = x.begin(); + expect_true(it == it2); + + ++it; + expect_true(!(it == it2)); + expect_true(it != it2); + + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex ninety_nine{99, 99}; + cpp11::r_complex ninety_eight{98, 98}; + cpp11::r_complex one_hundred{100, 100}; + + ++it; + expect_true(*it == three); + --it; + expect_true(*it == two); + --it; + + it += 99; + expect_true(*it == one_hundred); + --it; + expect_true(*it == ninety_nine); + --it; + expect_true(*it == ninety_eight); + it -= 95; + expect_true(*it == three); + } + + test_that("complexes.push_back()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + x.push_back(one); + x.push_back(two); + x.push_back(three); + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + } + test_that("complexes.resize()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + x.resize(3); + x[0] = one; + x[1] = two; + x[2] = three; + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + } + test_that("complexes.at()") { + cpp11::writable::complexes x; + + expect_error(x.at(-1)); + + expect_error(x.at(0)); + + cpp11::r_complex one{1, 1}; + + x.push_back(one); + expect_true(x.at(0) == one); + expect_error(x.at(1)); + } + test_that("complexes.pop_back()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x; + + x.push_back(one); + x.push_back(two); + x.pop_back(); + + expect_true(x.size() == 1); + expect_true(x[0] == one); + + expect_error(x.at(1)); + } + test_that("complexes.insert()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + + x.insert(0, one); + x.insert(0, two); + x.insert(1, three); + expect_true(x.size() == 3); + + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == one); + } + test_that("complexes.erase()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex four{4, 4}; + cpp11::r_complex five{5, 5}; + + cpp11::writable::complexes x; + + x.push_back(one); + x.push_back(two); + x.push_back(three); + x.push_back(four); + x.push_back(five); + + expect_true(x.size() == 5); + + x.erase(0); + + expect_true(x.size() == 4); + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == four); + expect_true(x[3] == five); + + x.erase(2); + + expect_true(x.size() == 3); + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == five); + } + test_that("complexes.iterator* = ") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex four{4, 4}; + + cpp11::writable::complexes x; + x.push_back(one); + x.push_back(two); + x.push_back(three); + auto it = x.begin() + 1; + *it = three; + ++it; + *it = four; + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == three); + expect_true(x[2] == four); + } + + test_that("writable::complexes(SEXP)") { + Rcomplex one{1, 1}; + Rcomplex two{2, 2}; + Rcomplex three{3, 3}; + Rcomplex four{4, 4}; + Rcomplex five{5, 5}; + Rcomplex six{6, 6}; + Rcomplex seven{7, 7}; + + SEXP x = PROTECT(Rf_allocVector(CPLXSXP, 5)); + + COMPLEX(x)[0] = one; + COMPLEX(x)[1] = two; + COMPLEX(x)[2] = three; + COMPLEX(x)[3] = four; + COMPLEX(x)[4] = five; + + cpp11::writable::complexes y(x); + y[0] = cpp11::r_complex(six); + + expect_true(x != y.data()); + + expect_true(COMPLEX(x)[0].r == one.r); + expect_true(COMPLEX(x)[0].i == one.i); + expect_true(y[0] == cpp11::r_complex(six)); + + cpp11::writable::complexes z(y); + z[0] = cpp11::r_complex(seven); + + expect_true(z.data() != y.data()); + + expect_true(COMPLEX(x)[0].r == one.r); + expect_true(COMPLEX(x)[0].i == one.i); + expect_true(y[0] == cpp11::r_complex(six)); + expect_true(z[0] == cpp11::r_complex(seven)); + + UNPROTECT(1); + } + test_that("writable::complexes(SEXP, bool)") { + Rcomplex five{5, 5}; + SEXP x = PROTECT(Rf_ScalarComplex(five)); + cpp11::writable::complexes y(x, false); + + expect_true(COMPLEX(y)[0].r == five.r); + expect_true(COMPLEX(y)[0].i == five.i); + UNPROTECT(1); + } + + test_that("writable::complexes(SEXP) assignment") { + cpp11::r_complex zero{0, 0}; + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x({one, two, three}); + cpp11::writable::complexes y({zero}); + y = x; + expect_true(y.size() == 3); + expect_true(y.data() != x.data()); + expect_true(y.is_altrep() == x.is_altrep()); + } + + test_that("writable::complexes(SEXP) move assignment") { + cpp11::r_complex zero{0, 0}; + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x({one, two, three}); + cpp11::writable::complexes y({zero}); + auto x_data = x.data(); + + y = std::move(x); + expect_true(y.size() == 3); + expect_true(y.data() == x_data); + expect_true(y.is_altrep() == false); + } + + test_that("complexes::names(empty)") { + cpp11::complexes x; + auto nms = x.names(); + expect_true(nms.size() == 0); + } + + test_that("complexes::names") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two, "c"_nm = three}); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + + expect_true(x.contains("a")); + expect_true(!x.contains("d")); + + expect_true(x["a"] == one); + expect_true(x["b"] == two); + expect_true(x["c"] == three); + + cpp11::sexp nms(x.names()); + expect_true(Rf_xlength(nms) == 3); + auto nms0 = CHAR(STRING_ELT(nms, 0)); + auto nms1 = CHAR(STRING_ELT(nms, 1)); + auto nms2 = CHAR(STRING_ELT(nms, 2)); + expect_true(strcmp(nms0, "a") == 0); + expect_true(strcmp(nms1, "b") == 0); + expect_true(strcmp(nms2, "c") == 0); + } + + test_that("complexes::attr") { + cpp11::complexes x(PROTECT(Rf_allocVector(CPLXSXP, 2))); + COMPLEX(x)[0] = Rcomplex{1, 1}; + COMPLEX(x)[1] = Rcomplex{2, 2}; + + SEXP foo = Rf_install("foo"); + Rf_setAttrib(x, foo, Rf_mkString("bar")); + + // This doesn't compile by design + // x.attr("foo") = "bar"; + + // But this will + cpp11::writable::complexes y(x); + y.attr("foo") = "baz"; + + expect_true(strcmp(CHAR(STRING_ELT(x.attr("foo"), 0)), "bar") == 0); + expect_true(strcmp(CHAR(STRING_ELT(y.attr("foo"), 0)), "baz") == 0); + + UNPROTECT(1); + } + + test_that("writable::complexes(std::vector::iterator)") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + std::vector x({one, two}); + cpp11::writable::complexes y(x.begin(), x.end()); + + expect_true(y.size() == 2); + expect_true(y[0] == one); + expect_true(y[1] == two); + } + + test_that("writable::complexes(std::vector)") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + std::vector x({one, two, three}); + cpp11::writable::complexes y(x); + + expect_true(y.size() == 3); + expect_true(y[0] == one); + expect_true(y[2] == three); + } + + test_that("writable::complexes attributes are kept when converted to complexes") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x({one, two}); + x.names() = {"a", "b"}; + cpp11::strings x_nms(x.names()); + expect_true(x_nms[0] == "a"); + expect_true(x_nms[1] == "b"); + + cpp11::complexes y(x); + cpp11::strings y_nms(y.names()); + expect_true(y_nms[0] == "a"); + expect_true(y_nms[1] == "b"); + } + + test_that("comparison operator works") { + using namespace cpp11; + + // SEXP base = PROTECT(Rf_allocVector(CPLXSXP, 2)); + // SEXP same_values = PROTECT(Rf_allocVector(CPLXSXP, 2)); + // SEXP diff_length = PROTECT(Rf_allocVector(CPLXSXP, 1)); + // SEXP diff_values = PROTECT(Rf_allocVector(CPLXSXP, 2)); + + cpp11::complexes base(Rf_allocVector(CPLXSXP, 2)); + cpp11::complexes same_values(Rf_allocVector(CPLXSXP, 2)); + cpp11::complexes diff_length(Rf_allocVector(CPLXSXP, 1)); + cpp11::complexes diff_values(Rf_allocVector(CPLXSXP, 2)); + + COMPLEX(base)[0] = Rcomplex{1, 1}; + COMPLEX(base)[1] = Rcomplex{2, 2}; + + COMPLEX(same_values)[0] = Rcomplex{1, 1}; + COMPLEX(same_values)[1] = Rcomplex{2, 2}; + + COMPLEX(diff_length)[0] = Rcomplex{1, 1}; + + COMPLEX(diff_values)[0] = Rcomplex{1, 1}; + COMPLEX(diff_values)[1] = Rcomplex{3, 3}; + + expect_true(base == base); + expect_true(base == same_values); + expect_true(!(base == diff_length)); + expect_true(!(base == diff_values)); + + expect_true(!(base != base)); + expect_true(!(base != same_values)); + expect_true(base != diff_length); + expect_true(base != diff_values); + + UNPROTECT(4); + } + + test_that("proxy comparison works symmetrically") { + cpp11::r_complex x{1, 2}; + cpp11::writable::complexes y({x}); + + expect_true(x == y[0]); + expect_true(y[0] == x); + } + + test_that("complexes operator[] and at") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::complexes x(Rf_allocVector(CPLXSXP, 2)); + COMPLEX(x)[0] = Rcomplex(one); + COMPLEX(x)[1] = Rcomplex(two); + + int i0 = 0; + R_xlen_t x0 = 0; + size_t s0 = 0; + + expect_true(x[i0] == one); + expect_true(x[x0] == one); + expect_true(x[s0] == one); + + expect_true(x.at(i0) == one); + expect_true(x.at(x0) == one); + expect_true(x.at(s0) == one); + } + + test_that("writable::complexes operator[] and at") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x(Rf_allocVector(CPLXSXP, 2)); + COMPLEX(x)[0] = Rcomplex(one); + COMPLEX(x)[1] = Rcomplex(two); + + int i0 = 0; + R_xlen_t x0 = 0; + size_t s0 = 0; + + expect_true(x[i0] == one); + expect_true(x[x0] == one); + expect_true(x[s0] == one); + + expect_true(x.at(i0) == one); + expect_true(x.at(x0) == one); + expect_true(x.at(s0) == one); + } + + test_that("operator[] and at with names") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two}); + cpp11::complexes y(x); + + expect_true(x["a"] == one); + expect_true(x["b"] == two); + expect_error(x["c"] == two); + + expect_true(y["a"] == one); + expect_true(y["b"] == two); + expect_error(y["c"] == two); + } + + test_that("complexes::find") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two}); + cpp11::complexes y(x); + + expect_true(x.find("a") == x.begin()); + expect_true(x.find("b") == x.begin() + 1); + expect_true(x.find("c") == x.end()); + + expect_true(y.find("a") == y.begin()); + expect_true(y.find("b") == y.begin() + 1); + expect_true(y.find("c") == y.end()); + } + + test_that("writable::complexes compound assignments") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x({one}); + + auto x0 = x[0]; + expect_true(x0 == one); + + // Arithmetic is not defined on Rcomplex or r_complex, + // so using it on a proxy also fails and is not defined + // expect_error(x0 += two); + // expect_error(x0 -= two); + // expect_error(x0 *= two); + // expect_error(x0 /= two); + // expect_error(x0--); + // expect_error(x0++); + // expect_error(++x0); + // expect_error(--x0); + } + + test_that("writable::complexes convert to complexes with correct size") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes foo; + foo.push_back(one); + foo.push_back(two); + foo.push_back(three); + + cpp11::complexes bar(foo); + expect_true(Rf_xlength(bar) == 3); + } +} diff --git a/cpp11test/src/test-doubles.cpp b/cpp11test/src/test-doubles.cpp index b17572b8..837103ee 100644 --- a/cpp11test/src/test-doubles.cpp +++ b/cpp11test/src/test-doubles.cpp @@ -3,6 +3,7 @@ #include "cpp11/doubles.hpp" #include "cpp11/function.hpp" #include "cpp11/integers.hpp" +#include "cpp11/logicals.hpp" #include "cpp11/sexp.hpp" #include "cpp11/strings.hpp" @@ -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/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/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/cpp11test/src/test-matrix.cpp b/cpp11test/src/test-matrix.cpp index 39e33938..18e62b71 100644 --- a/cpp11test/src/test-matrix.cpp +++ b/cpp11test/src/test-matrix.cpp @@ -1,3 +1,4 @@ +#include "cpp11/complexes.hpp" #include "cpp11/doubles.hpp" #include "cpp11/function.hpp" #include "cpp11/integers.hpp" @@ -24,6 +25,7 @@ context("matrix-C++") { expect_true(x[1].size() == 2); expect_true(x[1].stride() == 5); } + test_that("matrix dim attributes are correct for read only matrices") { auto getExportedValue = cpp11::package("base")["getExportedValue"]; @@ -41,6 +43,7 @@ context("matrix-C++") { expect_true(x[1].size() == 61); expect_true(x[1].stride() == 87); } + test_that("matrix attributes are correct") { cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); @@ -156,4 +159,40 @@ context("matrix-C++") { cpp11::writable::doubles_matrix x(5, 2); expect_error(cpp11::writable::integers_matrix(x)); } + + test_that("complex objects can be created, filled, and copied") { + // vector + + cpp11::writable::complexes v(2); + v[0] = std::complex(1, 2); + v[1] = std::complex(3, 4); + + cpp11::complexes vc = v; + + expect_true(v.size() == vc.size()); + + for (int i = 0; i < 2; ++i) { + expect_true(v[i] == vc[i]); + } + + // matrix + + cpp11::writable::complexes_matrix m(5, 2); + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 2; ++j) { + m(i, j) = std::complex(i, j); + } + } + + cpp11::complexes_matrix<> mc = m; + expect_true(m.nrow() == mc.nrow()); + expect_true(m.ncol() == mc.ncol()); + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 2; ++j) { + expect_true(m(i, j) == mc(i, j)); + } + } + } } diff --git a/cpp11test/src/test-r_complex.cpp b/cpp11test/src/test-r_complex.cpp new file mode 100644 index 00000000..3533eed6 --- /dev/null +++ b/cpp11test/src/test-r_complex.cpp @@ -0,0 +1,94 @@ +#include "cpp11/r_complex.hpp" + +#include "cpp11/sexp.hpp" + +#include + +context("r_complex-C++") { + test_that("r_complex() zero initialization") { + // `cpp11::r_complex x;` is "not initialized", this is "zero initialized" + cpp11::r_complex x{}; + expect_true(x.real() == 0.); + expect_true(x.imag() == 0.); + } + + test_that("r_complex(double, double) and accessors") { + cpp11::r_complex x(1., 2.); + expect_true(x.real() == 1.); + expect_true(x.imag() == 2.); + } + + test_that("r_complex(SEXP)") { + cpp11::r_complex x(1, 2); + + cpp11::sexp value = Rf_allocVector(CPLXSXP, 1); + COMPLEX(value)[0] = static_cast(x); + + cpp11::r_complex x2(value); + + expect_true(x2 == x); + } + + test_that("explicit construction from Rcomplex") { + Rcomplex x{1, 2}; + cpp11::r_complex y(x); + expect_true(y.real() == x.r); + expect_true(y.imag() == x.i); + } + + test_that("explicit construction from std::complex") { + std::complex x{1, 2}; + cpp11::r_complex y(x); + expect_true(y.real() == x.real()); + expect_true(y.imag() == x.imag()); + } + + test_that("explicit conversion to Rcomplex") { + cpp11::r_complex x(1, 2); + Rcomplex y(x); + expect_true(y.r == x.real()); + expect_true(y.i == x.imag()); + } + + test_that("explicit conversion to std::complex") { + cpp11::r_complex x(1, 2); + std::complex y(x); + expect_true(y.real() == x.real()); + expect_true(y.imag() == x.imag()); + } + + test_that("equality comparison of two r_complex") { + expect_true(cpp11::r_complex(1, 3) == cpp11::r_complex(1, 3)); + expect_false(cpp11::r_complex(1, 3) == cpp11::r_complex(2, 3)); + expect_false(cpp11::r_complex(1, 3) == cpp11::r_complex(1, 4)); + } + + test_that("na()") { + cpp11::r_complex x = cpp11::na(); + // Not `ISNA()`, checking specifically for `NA_REAL` + expect_true(R_IsNA(x.real())); + expect_true(R_IsNA(x.imag())); + } + + test_that("is_na(r_complex)") { + cpp11::r_complex x{1, 2}; + expect_false(cpp11::is_na(x)); + + cpp11::r_complex na_na{NA_REAL, NA_REAL}; + cpp11::r_complex na_real{NA_REAL, 1}; + cpp11::r_complex real_na{1, NA_REAL}; + + expect_true(cpp11::is_na(na_na)); + expect_true(cpp11::is_na(na_real)); + expect_true(cpp11::is_na(real_na)); + } + + test_that("as_sexp(r_complex)") { + cpp11::r_complex x{1, 2}; + cpp11::sexp value = cpp11::as_sexp(x); + + expect_true(Rf_xlength(value) == 1); + expect_true(COMPLEX(value)[0].r == x.real()); + expect_true(COMPLEX(value)[0].i == x.imag()); + } +} 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") diff --git a/cpp11test/tests/testthat/test-complex.R b/cpp11test/tests/testthat/test-complex.R new file mode 100644 index 00000000..6fec2e8c --- /dev/null +++ b/cpp11test/tests/testthat/test-complex.R @@ -0,0 +1,41 @@ +test_that("complex iterators work with normal vectors", { + len <- 1e5 + set.seed(42) + x <- complex(real = rnorm(len), imaginary = rnorm(len)) + sum_base <- sum(x) + + # Pacha: I know this is redundant, but exhanging equivalent types + # allowed me to test for errors in the implementation + expect_equal(sum_cplx_for_(x), sum_base) + expect_equal(sum_cplx_for_2_(x), sum_base) + expect_equal(sum_cplx_for_3_(x), sum_base) + expect_equal(sum_cplx_for_4_(x), sum_base) + expect_equal(sum_cplx_for_5_(x), sum_base) + expect_equal(sum_cplx_for_6_(x), sum_base) + + expect_equal(sum_cplx_foreach_(x), sum_base) + expect_equal(sum_cplx_accumulate_(x), sum_base) + expect_equal(sum_cplx_for2_(x), sum_base) +}) + +test_that("complex iterators work with altrep vectors", { + len <- 1e5 + seq_complex <- function(x) complex(real = as.double(seq_len(x)), imaginary = as.double(seq_len(x))) + + x <- seq_complex(len) + + sum_base <- sum(x) + + expect_equal(sum_cplx_for_(x), sum_base) + expect_equal(sum_cplx_foreach_(x), sum_base) + expect_equal(sum_cplx_accumulate_(x), sum_base) + expect_equal(sum_cplx_for2_(x), sum_base) +}) + +test_that("writable::complex grow", { + len <- 1e5L + expect_equal(grow_cplx_(len), complex( + real = as.numeric(seq(0, len - 1)), + imaginary = as.numeric(seq(0, len - 1)) + )) +}) 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/cpp11test/tests/testthat/test-map-to-list.R b/cpp11test/tests/testthat/test-map-to-list.R new file mode 100644 index 00000000..dd7fd5ca --- /dev/null +++ b/cpp11test/tests/testthat/test-map-to-list.R @@ -0,0 +1,21 @@ +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") + + om2 <- ordered_map_to_list_2_(x) + expect_equal(om, om2) + + 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/cpp11test/tests/testthat/test-matrix.R b/cpp11test/tests/testthat/test-matrix.R index a43f59b0..942d5ed8 100644 --- a/cpp11test/tests/testthat/test-matrix.R +++ b/cpp11test/tests/testthat/test-matrix.R @@ -23,3 +23,19 @@ test_that("col_sums gives same result as colSums", { y[3, ] <- NA; expect_equal(col_sums(y), colSums(y)) }) + +test_that("doubles_matrix<> can return a matrix with colnames and rownames", { + x <- matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2) + colnames(x) <- letters[1:2] + rownames(x) <- letters[3:4] + + y <- mat_mat_copy_dimnames(x) + z <- mat_sexp_copy_dimnames(x) + + expect_equal(x, y) + expect_equal(x, z) + + r <- mat_mat_create_dimnames() + expect_equal(rownames(r), c("a", "b")) + expect_equal(colnames(r), c("c", "d")) +}) diff --git a/inst/include/cpp11.hpp b/inst/include/cpp11.hpp index 71e1cf1d..95b7db3b 100644 --- a/inst/include/cpp11.hpp +++ b/inst/include/cpp11.hpp @@ -4,6 +4,7 @@ #include "cpp11/altrep.hpp" #include "cpp11/as.hpp" #include "cpp11/attribute_proxy.hpp" +#include "cpp11/complexes.hpp" #include "cpp11/data_frame.hpp" #include "cpp11/doubles.hpp" #include "cpp11/environment.hpp" @@ -17,6 +18,7 @@ #include "cpp11/named_arg.hpp" #include "cpp11/protect.hpp" #include "cpp11/r_bool.hpp" +#include "cpp11/r_complex.hpp" #include "cpp11/r_string.hpp" #include "cpp11/r_vector.hpp" #include "cpp11/raws.hpp" diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 682f12b5..6d453ef0 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -1,11 +1,15 @@ #pragma once #include // for modf +#include // for std::complex #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 @@ -184,8 +188,14 @@ template enable_if_c_string as_cpp(SEXP from) { if (Rf_isString(from)) { if (Rf_xlength(from) == 1) { - // TODO: use vmaxget / vmaxset here? - return {unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0)); })}; + void* vmax = vmaxget(); + + const char* result = + unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0)); }); + + vmaxset(vmax); + + return {result}; } } @@ -243,7 +253,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 { @@ -287,18 +297,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; @@ -325,7 +331,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 +339,62 @@ enable_if_convertible_to_sexp as_sexp(const T& from) { return from; } +// Definition for converting SEXP to std::complex +inline std::complex as_cpp(SEXP x) { + if (TYPEOF(x) != CPLXSXP || Rf_length(x) != 1) { + throw std::invalid_argument("Expected a single complex number."); + } + Rcomplex c = COMPLEX(x)[0]; + return {c.r, c.i}; +} + +// Specialization for converting std::complex to SEXP +inline SEXP as_sexp(const std::complex& x) { + SEXP result = PROTECT(Rf_allocVector(CPLXSXP, 1)); + COMPLEX(result)[0].r = x.real(); + COMPLEX(result)[0].i = x.imag(); + UNPROTECT(1); + return result; +} + +// 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, as_sexp(it->second)); + REAL(names)[i] = it->first; + } + + Rf_setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + +// 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, as_sexp(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/complexes.hpp b/inst/include/cpp11/complexes.hpp new file mode 100644 index 00000000..0e0dd69b --- /dev/null +++ b/inst/include/cpp11/complexes.hpp @@ -0,0 +1,253 @@ +#pragma once + +#include // for std::transform +#include // for std::complex +#include // for std::initializer_list + +#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector, COMPLEX, COMPLEX_ELT, SET_COMPLEX_ELT +#include "cpp11/as.hpp" // for as_sexp +#include "cpp11/protect.hpp" // for safe +#include "cpp11/r_complex.hpp" // for r_complex +#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy +#include "cpp11/sexp.hpp" // for sexp + +// Define SET_COMPLEX_ELT if not defined +// for compatibility with older R versions, such as ubuntu 20.04 oldrel-4 +#ifndef SET_COMPLEX_ELT +#define SET_COMPLEX_ELT(x, i, v) (COMPLEX(x)[i] = v) +#endif + +namespace cpp11 { + +// Specializations for complex numbers + +template <> +inline SEXPTYPE r_vector::get_sexptype() { + return CPLXSXP; +} + +template <> +inline typename r_vector::underlying_type r_vector::get_elt( + SEXP x, R_xlen_t i) { + return COMPLEX_ELT(x, i); +} + +template <> +inline typename r_vector::underlying_type* r_vector::get_p( + bool is_altrep, SEXP data) { + if (is_altrep) { + return nullptr; + } else { + return COMPLEX(data); + } +} + +template <> +inline typename r_vector::underlying_type const* +r_vector::get_const_p(bool is_altrep, SEXP data) { + return COMPLEX_OR_NULL(data); +} + +template <> +inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, + typename r_vector::underlying_type* buf) { + COMPLEX_GET_REGION(x, i, n, buf); +} + +template <> +inline bool r_vector::const_iterator::use_buf(bool is_altrep) { + return is_altrep; +} + +typedef r_vector complexes; + +namespace writable { + +template <> +inline void r_vector::set_elt( + SEXP x, R_xlen_t i, typename cpp11::r_vector::underlying_type value) { + COMPLEX(x)[i] = value; +} + +typedef r_vector complexes; + +} // namespace writable + +inline complexes as_complexes(SEXP x) { + if (detail::r_typeof(x) == CPLXSXP) { + return complexes(x); + } + + else if (detail::r_typeof(x) == INTSXP) { + r_vector xn(x); + size_t len = xn.size(); + writable::complexes ret(len); + std::transform(xn.begin(), xn.end(), ret.begin(), [](int value) { + return value == NA_INTEGER ? r_complex(NA_REAL, NA_REAL) + : r_complex(static_cast(value), 0.0); + }); + return ret; + } + + throw type_error(CPLXSXP, detail::r_typeof(x)); +} + +// Define comparison operators within the proxy class +namespace writable { + +template <> +class r_vector::proxy { + public: + proxy(SEXP data, R_xlen_t index) + : data_(data), index_(index), buf_(nullptr), is_altrep_(false) {} + + proxy(SEXP data, R_xlen_t index, Rcomplex* buf, bool is_altrep) + : data_(data), index_(index), buf_(buf), is_altrep_(is_altrep) {} + + operator r_complex() const { + if (is_altrep_ && buf_ != nullptr) { + return r_complex(buf_->r, buf_->i); + } else { + Rcomplex r = COMPLEX_ELT(data_, index_); + return r_complex(r.r, r.i); + } + } + + proxy& operator=(const r_complex& value) { + if (is_altrep_ && buf_ != nullptr) { + buf_->r = value.real(); + buf_->i = value.imag(); + } else { + Rcomplex r; + r.r = value.real(); + r.i = value.imag(); + SET_COMPLEX_ELT(data_, index_, r); + } + return *this; + } + + proxy& operator=(const std::complex& value) { + if (is_altrep_ && buf_ != nullptr) { + buf_->r = value.real(); + buf_->i = value.imag(); + } else { + Rcomplex r; + r.r = value.real(); + r.i = value.imag(); + SET_COMPLEX_ELT(data_, index_, r); + } + return *this; + } + + proxy& operator+=(const r_complex& value) { + *this = static_cast(*this) + value; + return *this; + } + + proxy& operator-=(const r_complex& value) { + *this = static_cast(*this) - value; + return *this; + } + + proxy& operator*=(const r_complex& value) { + *this = static_cast(*this) * value; + return *this; + } + + proxy& operator/=(const r_complex& value) { + *this = static_cast(*this) / value; + return *this; + } + + proxy& operator++() { + *this += r_complex(1, 0); + return *this; + } + + proxy operator++(int) { + proxy tmp(*this); + operator++(); + return tmp; + } + + proxy& operator--() { + *this -= r_complex(1, 0); + return *this; + } + + proxy operator--(int) { + proxy tmp(*this); + operator--(); + return tmp; + } + + friend bool operator==(const proxy& lhs, const r_complex& rhs) { + return static_cast(lhs) == rhs; + } + + friend bool operator!=(const proxy& lhs, const r_complex& rhs) { return !(lhs == rhs); } + + private: + SEXP data_; + R_xlen_t index_; + Rcomplex* buf_; + bool is_altrep_; +}; + +} // namespace writable + +// New complex_vector class for handling complex numbers in SEXP +class complex_vector { + public: + explicit complex_vector(SEXP x) + : data_(reinterpret_cast(DATAPTR(x))), size_(Rf_length(x)) {} + + std::complex operator[](R_xlen_t i) const { return {data_[i].r, data_[i].i}; } + + size_t size() const { return size_; } + + private: + Rcomplex* data_; + size_t size_; +}; + +// Template specialization for adding cpp11::r_complex to std::complex +template +inline std::complex& operator+=(std::complex& lhs, const cpp11::r_complex& rhs) { + lhs.real(lhs.real() + rhs.real()); + lhs.imag(lhs.imag() + rhs.imag()); + return lhs; +} + +// Add constructor for initializer_list +template <> +inline r_vector::r_vector(std::initializer_list il) { + R_xlen_t size = il.size(); + SEXP data = PROTECT(Rf_allocVector(CPLXSXP, size)); + auto it = il.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + Rcomplex r; + r.r = it->real(); + r.i = it->imag(); + COMPLEX(data)[i] = r; + } + UNPROTECT(1); + data_ = data; +} + +// Comparison operators for r_vector +template <> +inline bool operator==(const r_vector& lhs, const r_vector& rhs) { + if (lhs.size() != rhs.size()) return false; + for (R_xlen_t i = 0; i < lhs.size(); ++i) { + if (!(lhs[i] == rhs[i])) return false; + } + return true; +} + +template <> +inline bool operator!=(const r_vector& lhs, const r_vector& rhs) { + return !(lhs == rhs); +} + +} // namespace cpp11 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 diff --git a/inst/include/cpp11/doubles.hpp b/inst/include/cpp11/doubles.hpp index dcab086d..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,6 +85,14 @@ inline doubles as_doubles(SEXP x) { return value == NA_INTEGER ? NA_REAL : static_cast(value); }); return ret; + } else if (detail::r_typeof(x) == LGLSXP) { + logicals xn(x); + size_t len = xn.size(); + writable::doubles ret(len); + std::transform(xn.begin(), xn.end(), ret.begin(), [](bool value) { + return value == NA_LOGICAL ? NA_REAL : static_cast(value); + }); + return ret; } throw type_error(REALSXP, detail::r_typeof(x)); diff --git a/inst/include/cpp11/external_pointer.hpp b/inst/include/cpp11/external_pointer.hpp index a62134ec..95ce56d3 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)); @@ -38,7 +39,7 @@ class external_pointer { T* ptr = static_cast(R_ExternalPtrAddr(p)); - if (ptr == NULL) { + if (ptr == nullptr) { return; } @@ -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 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/integers.hpp b/inst/include/cpp11/integers.hpp index 68fd8e4d..a889fc6a 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,9 +98,40 @@ 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)); } } // 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/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/matrix.hpp b/inst/include/cpp11/matrix.hpp index 8345068f..70746ada 100644 --- a/inst/include/cpp11/matrix.hpp +++ b/inst/include/cpp11/matrix.hpp @@ -1,13 +1,15 @@ #pragma once +#include // for initializer_list #include #include // for string -#include "cpp11/R.hpp" // for SEXP, SEXPREC, R_xlen_t, INT... -#include "cpp11/r_bool.hpp" // for r_bool -#include "cpp11/r_string.hpp" // for r_string -#include "cpp11/r_vector.hpp" // for r_vector -#include "cpp11/sexp.hpp" // for sexp +#include "cpp11/R.hpp" // for SEXP, SEXPREC, R_xlen_t, INT... +#include "cpp11/r_bool.hpp" // for r_bool +#include "cpp11/r_complex.hpp" // for r_complex +#include "cpp11/r_string.hpp" // for r_string +#include "cpp11/r_vector.hpp" // for r_vector +#include "cpp11/sexp.hpp" // for sexp namespace cpp11 { @@ -188,13 +190,49 @@ class matrix : public matrix_slices { operator SEXP() const { return SEXP(vector_); } - // operator sexp() { return sexp(vector_); } + attribute_proxy attr(const char* name) { return attribute_proxy(vector_, name); } - sexp attr(const char* name) const { return SEXP(vector_.attr(name)); } + attribute_proxy attr(const std::string& name) { + return attribute_proxy(vector_, name.c_str()); + } + + attribute_proxy attr(SEXP name) { return attribute_proxy(vector_, name); } + + void attr(const char* name, SEXP value) { vector_.attr(name) = value; } + + void attr(const std::string& name, SEXP value) { vector_.attr(name) = value; } + + void attr(SEXP name, SEXP value) { vector_.attr(name) = value; } + + void attr(const char* name, std::initializer_list value) { + SEXP attr = PROTECT(Rf_allocVector(VECSXP, value.size())); + int i = 0; + for (SEXP v : value) { + SET_VECTOR_ELT(attr, i++, v); + } + vector_.attr(name) = attr; + UNPROTECT(1); + } - sexp attr(const std::string& name) const { return SEXP(vector_.attr(name)); } + void attr(const std::string& name, std::initializer_list value) { + SEXP attr = PROTECT(Rf_allocVector(VECSXP, value.size())); + int i = 0; + for (SEXP v : value) { + SET_VECTOR_ELT(attr, i++, v); + } + vector_.attr(name) = attr; + UNPROTECT(1); + } - sexp attr(SEXP name) const { return SEXP(vector_.attr(name)); } + void attr(SEXP name, std::initializer_list value) { + SEXP attr = PROTECT(Rf_allocVector(VECSXP, value.size())); + int i = 0; + for (SEXP v : value) { + SET_VECTOR_ELT(attr, i++, v); + } + vector_.attr(name) = attr; + UNPROTECT(1); + } r_vector names() const { return r_vector(vector_.names()); } @@ -214,6 +252,8 @@ template using logicals_matrix = matrix, r_bool, S>; template using strings_matrix = matrix, r_string, S>; +template +using complexes_matrix = matrix, r_complex, S>; namespace writable { template @@ -224,6 +264,8 @@ template using logicals_matrix = matrix, r_vector::proxy, S>; template using strings_matrix = matrix, r_vector::proxy, S>; +template +using complexes_matrix = matrix, r_vector::proxy, S>; } // namespace writable // TODO: Add tests for Matrix class diff --git a/inst/include/cpp11/r_complex.hpp b/inst/include/cpp11/r_complex.hpp new file mode 100644 index 00000000..89957aa7 --- /dev/null +++ b/inst/include/cpp11/r_complex.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include // for std::complex + +#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_mkCharCE, Rf_translateCharUTF8 +#include "cpp11/as.hpp" // for as_sexp +#include "cpp11/protect.hpp" // for unwind_protect, protect, protect::function +#include "cpp11/sexp.hpp" // for sexp + +namespace cpp11 { + +class r_complex { + public: + r_complex() : data_(safe[Rf_allocVector](CPLXSXP, 1)) { + COMPLEX(data_)[0].r = 0; + COMPLEX(data_)[0].i = 0; + } + r_complex(SEXP data) : data_(data) { + if (data_ == R_NilValue) { + data_ = PROTECT(Rf_allocVector(CPLXSXP, 0)); + UNPROTECT(1); + } + } + r_complex(double real, double imag) : data_(safe[Rf_allocVector](CPLXSXP, 1)) { + COMPLEX(data_)[0].r = real; + COMPLEX(data_)[0].i = imag; + } + r_complex(const std::complex& data) : r_complex(data.real(), data.imag()) {} + r_complex(const Rcomplex& data) : r_complex(data.r, data.i) {} + + operator SEXP() const { return data_; } + operator sexp() const { return data_; } + operator std::complex() const { + if (data_ == R_NilValue || Rf_length(data_) == 0) { + return {NA_REAL, NA_REAL}; + } + return {COMPLEX(data_)[0].r, COMPLEX(data_)[0].i}; + } + operator Rcomplex() const { + Rcomplex r; + if (data_ == R_NilValue || Rf_length(data_) == 0) { + r.r = NA_REAL; + r.i = NA_REAL; + } else { + r.r = real(); + r.i = imag(); + } + return r; + } + + double real() const { + if (data_ == R_NilValue || Rf_length(data_) == 0) { + return NA_REAL; + } + return COMPLEX(data_)[0].r; + } + double imag() const { + if (data_ == R_NilValue || Rf_length(data_) == 0) { + return NA_REAL; + } + return COMPLEX(data_)[0].i; + } + + bool operator==(const r_complex& rhs) const { + return (is_na() && rhs.is_na()) || (real() == rhs.real() && imag() == rhs.imag()); + } + + bool operator!=(const r_complex& rhs) const { return !(*this == rhs); } + + r_complex& operator+=(const r_complex& rhs) { + *this = r_complex(real() + rhs.real(), imag() + rhs.imag()); + return *this; + } + + r_complex& operator-=(const r_complex& rhs) { + *this = r_complex(real() - rhs.real(), imag() - rhs.imag()); + return *this; + } + + r_complex& operator*=(const r_complex& rhs) { + std::complex lhs = *this; + lhs *= static_cast>(rhs); + *this = r_complex(lhs.real(), lhs.imag()); + return *this; + } + + r_complex& operator/=(const r_complex& rhs) { + std::complex lhs = *this; + lhs /= static_cast>(rhs); + *this = r_complex(lhs.real(), lhs.imag()); + return *this; + } + + friend r_complex operator+(r_complex lhs, const r_complex& rhs) { + lhs += rhs; + return lhs; + } + + friend r_complex operator-(r_complex lhs, const r_complex& rhs) { + lhs -= rhs; + return lhs; + } + + friend r_complex operator*(r_complex lhs, const r_complex& rhs) { + lhs *= rhs; + return lhs; + } + + friend r_complex operator/(r_complex lhs, const r_complex& rhs) { + lhs /= rhs; + return lhs; + } + + bool is_na() const { return R_IsNA(real()) || R_IsNA(imag()); } + + private: + sexp data_ = R_NilValue; +}; + +inline SEXP as_sexp(const r_complex& from) { + sexp res; + unwind_protect([&] { + res = Rf_allocVector(CPLXSXP, 1); + COMPLEX(res)[0].r = from.real(); + COMPLEX(res)[0].i = from.imag(); + }); + + return res; +} + +inline SEXP as_sexp(std::initializer_list il) { + R_xlen_t size = il.size(); + + sexp data; + unwind_protect([&] { + data = Rf_allocVector(CPLXSXP, size); + auto it = il.begin(); + for (R_xlen_t i = 0; i < size; ++i, ++it) { + COMPLEX(data)[i].r = it->real(); + COMPLEX(data)[i].i = it->imag(); + } + }); + return data; +} + +template <> +inline r_complex na() { + return r_complex(NA_REAL, NA_REAL); +} + +namespace traits { +template <> +struct get_underlying_type { + using type = Rcomplex; +}; +} // namespace traits + +} // namespace cpp11 diff --git a/inst/include/cpp11/r_vector.hpp b/inst/include/cpp11/r_vector.hpp index 576f4fe6..8a9ab954 100644 --- a/inst/include/cpp11/r_vector.hpp +++ b/inst/include/cpp11/r_vector.hpp @@ -18,6 +18,7 @@ #include "cpp11/attribute_proxy.hpp" // for attribute_proxy #include "cpp11/named_arg.hpp" // for named_arg #include "cpp11/protect.hpp" // for store +#include "cpp11/r_complex.hpp" // for r_complex #include "cpp11/r_string.hpp" // for r_string #include "cpp11/sexp.hpp" // for sexp @@ -30,7 +31,6 @@ template class r_vector; } // namespace writable -// Declarations template class r_vector { public: @@ -60,6 +60,7 @@ class r_vector { r_vector(const r_vector& x); r_vector(r_vector&& x); r_vector(const writable::r_vector& x); + r_vector(std::initializer_list il); r_vector(named_arg) = delete; r_vector& operator=(const r_vector& rhs); @@ -235,8 +236,12 @@ 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); + // Implemented in `complexes.hpp` + void push_back(const Rcomplex& value); void pop_back(); void resize(R_xlen_t count); @@ -256,6 +261,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; @@ -865,7 +879,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(); @@ -876,20 +891,30 @@ 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); SET_STRING_ELT(names, i, name); } + + UNPROTECT(1); }); } @@ -1156,6 +1181,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); @@ -1392,18 +1435,14 @@ inline SEXP r_vector::resize_names(SEXP x, R_xlen_t size) { } // namespace writable -// TODO: is there a better condition we could use, e.g. assert something true -// rather than three things false? -template -using is_container_but_not_sexp_or_string = typename std::enable_if< +// Ensure that C is not constructible from SEXP, and neither C nor T is a std::string +template ::type::value_type> +typename std::enable_if< !std::is_constructible::value && !std::is_same::type, std::string>::value && !std::is_same::type, std::string>::value, - typename std::decay::type>::type; - -template ::type::value_type> -// typename T = typename C::value_type> -is_container_but_not_sexp_or_string as_cpp(SEXP from) { + C>::type +as_cpp(SEXP from) { auto obj = cpp11::r_vector(from); return {obj.begin(), obj.end()}; } diff --git a/inst/include/cpp11/sexp.hpp b/inst/include/cpp11/sexp.hpp index 74205e69..6b3bbc20 100644 --- a/inst/include/cpp11/sexp.hpp +++ b/inst/include/cpp11/sexp.hpp @@ -2,7 +2,8 @@ #include // for size_t -#include // for string, basic_string +#include // for complex +#include // for string, basic_string #include "cpp11/R.hpp" // for SEXP, SEXPREC, REAL_ELT, R_NilV... #include "cpp11/attribute_proxy.hpp" // for attribute_proxy diff --git a/inst/include/cpp11/strings.hpp b/inst/include/cpp11/strings.hpp index 393f2a8c..fdf84405 100644 --- a/inst/include/cpp11/strings.hpp +++ b/inst/include/cpp11/strings.hpp @@ -61,12 +61,24 @@ 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 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); + } + set_elt(this->data_, this->length_, + Rf_mkCharLenCE(value.c_str(), value.size(), CE_UTF8)); + ++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 +107,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 +129,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 +148,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 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