├── .Rbuildignore ├── .clang-format ├── .covrignore ├── .gitattributes ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ └── issue_template.md ├── SUPPORT.md └── workflows │ ├── R-CMD-check.yaml │ ├── format.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── MAINTENANCE.md ├── Makefile ├── NAMESPACE ├── NEWS.md ├── R ├── coverage.R ├── cpp11-package.R ├── knitr.R ├── register.R ├── source.R ├── utils.R ├── vendor.R └── zzz.R ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cpp11.Rproj ├── cpp11test ├── .here ├── DESCRIPTION ├── NAMESPACE ├── R │ ├── catch-routine-registration.R │ ├── cpp11.R │ ├── cpp11test-package.R │ ├── should_run_benchmarks.R │ └── test.R ├── bench │ ├── grow.R │ ├── insert.R │ ├── protect.R │ ├── sum.R │ └── truncate.R ├── man │ ├── cpp11test-package.Rd │ └── run_tests.Rd ├── src │ ├── add.cpp │ ├── cpp11.cpp │ ├── data_frame.cpp │ ├── errors.cpp │ ├── errors_fmt.cpp │ ├── find-intervals.cpp │ ├── grow.cpp │ ├── insert.cpp │ ├── matrix.cpp │ ├── protect.cpp │ ├── release.cpp │ ├── safe.cpp │ ├── strings.cpp │ ├── sum.cpp │ ├── sum_int.cpp │ ├── sum_rcpp.cpp │ ├── test-as.cpp │ ├── test-data_frame.cpp │ ├── test-doubles.cpp │ ├── test-environment.cpp │ ├── test-external_pointer.cpp │ ├── test-function.cpp │ ├── test-integers.cpp │ ├── test-list.cpp │ ├── test-list_of.cpp │ ├── test-logicals.cpp │ ├── test-matrix.cpp │ ├── test-nas.cpp │ ├── test-protect-nested.cpp │ ├── test-protect.cpp │ ├── test-r_vector.cpp │ ├── test-raws.cpp │ ├── test-runner.cpp │ ├── test-sexp.cpp │ ├── test-string.cpp │ ├── test-strings.cpp │ └── truncate.cpp └── tests │ ├── testthat.R │ └── testthat │ ├── test-cpp.R │ ├── test-doubles.R │ ├── test-matrix.R │ └── test_formatted_errors.R ├── cran-comments.md ├── inst └── include │ ├── cpp11.hpp │ ├── cpp11 │ ├── R.hpp │ ├── altrep.hpp │ ├── as.hpp │ ├── attribute_proxy.hpp │ ├── data_frame.hpp │ ├── declarations.hpp │ ├── doubles.hpp │ ├── environment.hpp │ ├── external_pointer.hpp │ ├── function.hpp │ ├── integers.hpp │ ├── list.hpp │ ├── list_of.hpp │ ├── logicals.hpp │ ├── matrix.hpp │ ├── named_arg.hpp │ ├── protect.hpp │ ├── r_bool.hpp │ ├── r_string.hpp │ ├── r_vector.hpp │ ├── raws.hpp │ ├── sexp.hpp │ └── strings.hpp │ └── fmt │ ├── core.h │ ├── format-inl.h │ └── format.h ├── man ├── cpp11-package.Rd ├── cpp_register.Rd ├── cpp_source.Rd └── cpp_vendor.Rd ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── email.yml ├── failures.md └── problems.md ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── register.md │ └── source.md │ ├── helper.R │ ├── linking_to_incorrect_registers.cpp │ ├── linking_to_registers.cpp │ ├── multiple.cpp │ ├── multiple_incorrect.cpp │ ├── single.cpp │ ├── single_error.cpp │ ├── single_incorrect.cpp │ ├── test-knitr.R │ ├── test-register.R │ ├── test-source.R │ ├── test-utils.R │ └── test-vendor.R └── vignettes ├── .gitignore ├── FAQ.Rmd ├── converting.Rmd ├── cpp11.Rmd ├── growth.Rds ├── internals.Rmd ├── motivations.Rmd ├── release.Rds └── sum.Rds /.Rbuildignore: -------------------------------------------------------------------------------- 1 | script.R 2 | ^README\.Rmd$ 3 | ^README\.html$ 4 | ^\.github$ 5 | ^README_cache$ 6 | ^.covrignore$ 7 | ^.clang-format$ 8 | ^LICENSE\.md$ 9 | ^Doxyfile$ 10 | ^.*\.Rproj$ 11 | ^\.Rproj\.user$ 12 | ^Makefile$ 13 | ^\.clangd$ 14 | ^compile_flags\.txt$ 15 | ^compile_commands\.json$ 16 | ^cpp11test$ 17 | ^codecov\.yml$ 18 | ^R/coverage\.R$ 19 | ^book$ 20 | ^vignettes/.*_cache$ 21 | ^_pkgdown\.yml$ 22 | ^cran-comments\.md$ 23 | ^CRAN-RELEASE$ 24 | ^revdep$ 25 | ^talk$ 26 | \.cache$ 27 | ^MAINTENANCE\.md$ 28 | ^doc$ 29 | ^Meta$ 30 | ^CRAN-SUBMISSION$ 31 | .vscode 32 | ^\.cache$ 33 | ^docs$ 34 | ^pkgdown$ 35 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | DerivePointerAlignment: false 3 | ColumnLimit: 90 4 | IncludeBlocks: Preserve 5 | -------------------------------------------------------------------------------- /.covrignore: -------------------------------------------------------------------------------- 1 | R/coverage[.]R 2 | R/zzz.R 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | inst/include/fmt/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at [INSERT CONTACT 63 | METHOD]. All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at https://www.contributor-covenant.org/version/2/0/ 119 | code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at https:// 128 | www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to cpp11 2 | 3 | This outlines how to propose a change to cpp11. 4 | For more detailed info about contributing to this, and other tidyverse packages, please see the 5 | [**development contributing guide**](https://rstd.io/tidy-contrib). 6 | 7 | ## Fixing typos 8 | 9 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 10 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 11 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 12 | 13 | ## Bigger changes 14 | 15 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 16 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 17 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 18 | 19 | ### Pull request process 20 | 21 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("r-lib/cpp11", fork = TRUE)`. 22 | 23 | * Install all development dependences with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 24 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 25 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 26 | 27 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 28 | The title of your PR should briefly describe the change. 29 | The body of your PR should contain `Fixes #issue-number`. 30 | 31 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 32 | 33 | ### Code style 34 | 35 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 36 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. 37 | 38 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 39 | 40 | * We use [testthat](https://cran.r-project.org/package=testthat) for unit tests. 41 | Contributions with test cases included are easier to accept. 42 | 43 | ## Code of Conduct 44 | 45 | Please note that the cpp11 project is released with a 46 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 47 | project you agree to abide by its terms. 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report or feature request 3 | about: Describe a bug you've seen or make a case for a new feature 4 | --- 5 | 6 | Please briefly describe your problem and what output you expect. If you have a question, please don't use this form. Instead, ask on or . 7 | 8 | Please include a minimal reproducible example (AKA a reprex). If you've never heard of a [reprex](http://reprex.tidyverse.org/) before, start by reading . 9 | 10 | Brief description of the problem 11 | 12 | ```r 13 | # insert reprex here 14 | ``` 15 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting help with cpp11 2 | 3 | Thanks for using cpp11! 4 | Before filing an issue, there are a few places to explore and pieces to put together to make the process as smooth as possible. 5 | 6 | ## Make a reprex 7 | 8 | Start by making a minimal **repr**oducible **ex**ample using the [reprex](https://reprex.tidyverse.org/) package. 9 | If you haven't heard of or used reprex before, you're in for a treat! 10 | Seriously, reprex will make all of your R-question-asking endeavors easier (which is a pretty insane ROI for the five to ten minutes it'll take you to learn what it's all about). 11 | For additional reprex pointers, check out the [Get help!](https://www.tidyverse.org/help/) section of the tidyverse site. 12 | 13 | ## Where to ask? 14 | 15 | Armed with your reprex, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). 16 | 17 | * If it's a question: start with [community.rstudio.com](https://community.rstudio.com/), and/or StackOverflow. There are more people there to answer questions. 18 | 19 | * If it's a bug: you're in the right place, [file an issue](https://github.com/r-lib/cpp11/issues/new). 20 | 21 | * If you're not sure: let the community help you figure it out! 22 | If your problem _is_ a bug or a feature request, you can easily return here and report it. 23 | 24 | Before opening a new issue, be sure to [search issues and pull requests](https://github.com/r-lib/cpp11/issues) to make sure the bug hasn't been reported and/or already fixed in the development version. 25 | By default, the search will be pre-populated with `is:issue is:open`. 26 | You can [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/) (e.g. `is:pr`, `is:closed`) as needed. 27 | For example, you'd simply remove `is:open` to search _all_ issues in the repo, open or closed. 28 | 29 | ## What happens next? 30 | 31 | To be as efficient as possible, development of tidyverse packages tends to be very bursty, so you shouldn't worry if you don't get an immediate response. 32 | Typically we don't look at a repo until a sufficient quantity of issues accumulates, then there’s a burst of intense activity as we focus our efforts. 33 | That makes development more efficient because it avoids expensive context switching between problems, at the cost of taking longer to get back to you. 34 | This process makes a good reprex particularly important because it might be multiple months between your initial report and when we start working on it. 35 | If we can’t reproduce the bug, we can’t fix it! 36 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) ${{ matrix.config.custom }} 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - {os: macos-13, r: 'oldrel'} 28 | - {os: macos-latest, r: 'release'} 29 | 30 | - {os: windows-latest, r: 'release'} 31 | # use 4.1 to check with rtools40's older compiler 32 | - {os: windows-latest, r: '4.1'} 33 | 34 | # Use older ubuntu to maximise backward compatibility 35 | - {os: ubuntu-22.04, r: 'devel', http-user-agent: 'release'} 36 | - {os: ubuntu-22.04, r: 'release'} 37 | - {os: ubuntu-22.04, r: 'release', custom: 'no-cpp11test'} 38 | - {os: ubuntu-22.04, r: 'oldrel-1'} 39 | - {os: ubuntu-22.04, r: 'oldrel-2'} 40 | - {os: ubuntu-22.04, r: 'oldrel-3'} 41 | - {os: ubuntu-22.04, r: 'oldrel-4'} 42 | 43 | env: 44 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 45 | R_KEEP_PKG_SOURCE: yes 46 | 47 | steps: 48 | - uses: actions/checkout@v4 49 | 50 | - uses: r-lib/actions/setup-pandoc@v2 51 | 52 | - uses: r-lib/actions/setup-r@v2 53 | with: 54 | r-version: ${{ matrix.config.r }} 55 | http-user-agent: ${{ matrix.config.http-user-agent }} 56 | use-public-rspm: true 57 | 58 | - name: Install macOS system dependencies 59 | if: runner.os == 'macOS' 60 | run: brew install --cask xquartz 61 | 62 | - uses: r-lib/actions/setup-r-dependencies@v2 63 | with: 64 | extra-packages: any::rcmdcheck 65 | pak-version: devel 66 | needs: check 67 | 68 | - name: Install cpp11test 69 | if: matrix.config.custom != 'no-cpp11test' 70 | run: | 71 | options(warn = 2) 72 | pak::local_install_dev_deps("cpp11test", dependencies = TRUE) 73 | install.packages(".", repos = NULL, type = "source") 74 | install.packages("cpp11test", repos = NULL, INSTALL_opts = "--install-tests", type = "source") 75 | shell: Rscript {0} 76 | 77 | - uses: r-lib/actions/check-r-package@v2 78 | with: 79 | upload-snapshots: true 80 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 81 | 82 | - name: Run cpp11test tests 83 | if: matrix.config.custom != 'no-cpp11test' 84 | run: | 85 | setwd("cpp11test/tests") 86 | library(testthat) 87 | library(cpp11test) 88 | test_check("cpp11test", reporter = "progress") 89 | shell: Rscript {0} 90 | -------------------------------------------------------------------------------- /.github/workflows/format.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: main 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | name: format_check 9 | 10 | jobs: 11 | format_check: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install ClangFormat 17 | run: sudo apt-get install -y clang-format-12 18 | 19 | - name: Run ClangFormat 20 | run: make format clang_format=clang-format-12 21 | 22 | - name: Check for a non-empty diff 23 | run: git diff-files -U --exit-code 24 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::., local::cpp11test 38 | needs: website 39 | 40 | - name: Build site 41 | env: 42 | CPP11_EVAL: true 43 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 44 | shell: Rscript {0} 45 | 46 | - name: Deploy to GitHub pages 🚀 47 | if: github.event_name != 'pull_request' 48 | uses: JamesIves/github-pages-deploy-action@v4.5.0 49 | with: 50 | clean: false 51 | branch: gh-pages 52 | folder: docs 53 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/pr-fetch@v2 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - uses: r-lib/actions/setup-r-dependencies@v2 30 | with: 31 | extra-packages: any::roxygen2 32 | needs: pr-document 33 | 34 | - name: Document 35 | run: roxygen2::roxygenise() 36 | shell: Rscript {0} 37 | 38 | - name: commit 39 | run: | 40 | git config --local user.name "$GITHUB_ACTOR" 41 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 42 | git add man/\* NAMESPACE 43 | git commit -m 'Document' 44 | 45 | - uses: r-lib/actions/pr-push@v2 46 | with: 47 | repo-token: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | style: 50 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 51 | name: style 52 | runs-on: ubuntu-latest 53 | env: 54 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 55 | steps: 56 | - uses: actions/checkout@v4 57 | 58 | - uses: r-lib/actions/pr-fetch@v2 59 | with: 60 | repo-token: ${{ secrets.GITHUB_TOKEN }} 61 | 62 | - uses: r-lib/actions/setup-r@v2 63 | 64 | - name: Install dependencies 65 | run: install.packages("styler") 66 | shell: Rscript {0} 67 | 68 | - name: Style 69 | run: styler::style_pkg() 70 | shell: Rscript {0} 71 | 72 | - name: commit 73 | run: | 74 | git config --local user.name "$GITHUB_ACTOR" 75 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 76 | git add \*.R 77 | git commit -m 'Style' 78 | 79 | - uses: r-lib/actions/pr-push@v2 80 | with: 81 | repo-token: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Install cpp11test 32 | run: | 33 | options(warn = 2) 34 | pak::local_install_dev_deps("cpp11test", dependencies = TRUE) 35 | install.packages(".", repos = NULL, type = "source") 36 | install.packages("cpp11test", repos = NULL, INSTALL_opts = "--install-tests", type = "source") 37 | shell: Rscript {0} 38 | 39 | - name: Test coverage 40 | run: | 41 | cov <- covr::package_coverage( 42 | quiet = FALSE, 43 | clean = FALSE, 44 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 45 | ) 46 | covr::to_cobertura(cov) 47 | shell: Rscript {0} 48 | 49 | - uses: codecov/codecov-action@v4 50 | with: 51 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 52 | file: ./cobertura.xml 53 | plugin: noop 54 | disable_search: true 55 | token: ${{ secrets.CODECOV_TOKEN }} 56 | 57 | - name: Show testthat output 58 | if: always() 59 | run: | 60 | ## -------------------------------------------------------------------- 61 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 62 | shell: bash 63 | 64 | - name: Upload test results 65 | if: failure() 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: coverage-test-failures 69 | path: ${{ runner.temp }}/package 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | script.R 2 | README.html 3 | .Rproj.user 4 | .Rhistory 5 | .clangd/ 6 | cpp11test/compile_commands.json 7 | cpp11test/src/Makevars 8 | cpp11test/src/*.o 9 | cpp11test/src/*.so 10 | compile_flags.txt 11 | inst/doc 12 | vignettes/*_cache 13 | compile_commands.json 14 | revdep/*/ 15 | talk/ 16 | TAGS 17 | .cache/ 18 | /doc/ 19 | /Meta/ 20 | .vscode 21 | .cache 22 | docs 23 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: cpp11 2 | Title: A C++11 Interface for R's C Interface 3 | Version: 0.5.2.9000 4 | Authors@R: 5 | c( 6 | person("Davis", "Vaughan", email = "davis@posit.co", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-4777-038X")), 7 | person("Jim","Hester", role = "aut", comment = c(ORCID = "0000-0002-2739-7082")), 8 | person("Romain", "François", role = "aut", comment = c(ORCID = "0000-0002-2444-4226")), 9 | person("Benjamin", "Kietzman", role = "ctb"), 10 | person("Posit Software, PBC", role = c("cph", "fnd")) 11 | ) 12 | Description: Provides a header only, C++11 interface to R's C 13 | interface. Compared to other approaches 'cpp11' strives to be safe 14 | against long jumps from the C API as well as C++ exceptions, conform 15 | to normal R function semantics and supports interaction with 'ALTREP' 16 | vectors. 17 | License: MIT + file LICENSE 18 | URL: https://cpp11.r-lib.org, https://github.com/r-lib/cpp11 19 | BugReports: https://github.com/r-lib/cpp11/issues 20 | Depends: 21 | R (>= 4.0.0) 22 | Suggests: 23 | bench, 24 | brio, 25 | callr, 26 | cli, 27 | covr, 28 | decor, 29 | desc, 30 | ggplot2, 31 | glue, 32 | knitr, 33 | lobstr, 34 | mockery, 35 | progress, 36 | rmarkdown, 37 | scales, 38 | Rcpp, 39 | testthat (>= 3.2.0), 40 | tibble, 41 | utils, 42 | vctrs, 43 | withr 44 | VignetteBuilder: 45 | knitr 46 | Config/Needs/website: tidyverse/tidytemplate 47 | Config/testthat/edition: 3 48 | Config/Needs/cpp11/cpp_register: 49 | brio, 50 | cli, 51 | decor, 52 | desc, 53 | glue, 54 | tibble, 55 | vctrs 56 | Encoding: UTF-8 57 | Roxygen: list(markdown = TRUE) 58 | RoxygenNote: 7.3.2 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Posit Software, PBC 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020 RStudio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | ## Current state 2 | 3 | The state of cpp11 is pretty stable, it seems to have the features we need for most of our projects using C++. 4 | 5 | ## Known outstanding issues 6 | 7 | ### Running the cpp11test tests 8 | 9 | Most of the test suite is in a sub-package, cpp11test. 10 | The best way to run these tests is to install the development version of cpp11 after any change, and then run `devtools::test("./cpp11test")`. 11 | Precisely, this looks like: 12 | 13 | ```r 14 | # Install dev cpp11, clean the cpp11test dll manually since it thinks nothing 15 | # has changed, then recompile and run its tests. 16 | devtools::install() 17 | devtools::clean_dll("./cpp11test") 18 | devtools::test("./cpp11test") 19 | ``` 20 | 21 | If tests failures occur the output from Catch isn't always easy to interpret. 22 | I have a branch of testthat https://github.com/jimhester/testthat/tree/catch-detailed-output that should make things easier to understand. 23 | I contributed those changes to the main testthat, but something changed after merging the more detailed output was lost, I unfortunately never had the time to track down the cause and fix it. 24 | 25 | In addition getting a debugger to catch when errors happen can be fiddly when running the cpp11test tests, something about the way that Catch redirects stderr / stdout interacts with the debugger. 26 | 27 | The GitHub Actions workflow has some additional logic to handle running the cpp11 tests https://github.com/r-lib/cpp11/blob/fd8ef97d006db847f7f17166cf52e1e0383b2d35/.github/workflows/R-CMD-check.yaml#L95-L102, https://github.com/r-lib/cpp11/blob/fd8ef97d006db847f7f17166cf52e1e0383b2d35/.github/workflows/R-CMD-check.yaml#L117-L124. 28 | 29 | ## Ensure you use `Sys.setenv("CPP11_EVAL" = "true"); devtools::submit_cran()` when submitting. 30 | 31 | If you forget to set `CPP_EVAL = "true"` then the vignette chunks will not run properly and the vignettes will not be rendered properly. 32 | 33 | ## Regenerating benchmark objects used in `motivations.Rmd` 34 | 35 | If you need to regenerate the benchmark objects (RDS objects) utilized in `motivations.Rmd`, then you should set `Sys.setenv("CPP11TEST_SHOULD_RUN_BENCHMARKS" = "TRUE")` before running the Rmd. You'll also need to make sure that cpp11test is actually installed. See `cpp11test:::should_run_benchmarks()` for more. 36 | 37 | ## Usage with clangd 38 | 39 | Since cpp11 is header only, if you use clangd you'll have a bit of an issue because tools like bear and pkgload won't know how to generate the `compile_commands.json` file. Instead, you can create it manually with something like this, which seems to work well. Note that the paths are specific to your computer. 40 | 41 | ``` 42 | [ 43 | { 44 | "command": "g++ -std=gnu++11 -I\"/Users/davis/files/r/packages/cpp11/inst/include\" -I\"/Library/Frameworks/R.framework/Resources/include\" -I\"/Users/davis/Library/R/arm64/4.4/library/Rcpp/include\" -I\"/Users/davis/Library/R/arm64/4.4/library/testthat/include\" -I\"/opt/homebrew/include\" -Wall -pedantic", 45 | "file": "R.hpp", 46 | "directory": "/Users/davis/files/r/packages/cpp11/inst/include/cpp11" 47 | } 48 | ] 49 | ``` 50 | 51 | Key notes: 52 | 53 | - Only doing this for `R.hpp` seems to be enough. I imagine this could be any of the header files, but it is reasonable to pick the "root" one that pretty much all others include. 54 | - Using `-std=gnu++11` to keep us honest about only C++11 features. 55 | - Using `-I\"/Library/Frameworks/R.framework/Resources/include\"` for access to the R headers. 56 | - Using `-I\"/Users/davis/files/r/packages/cpp11/inst/include\"` as a "self include", which seems to be the key to the whole thing. 57 | 58 | If you are modifying any tests or benchmarks, you also need: 59 | 60 | - `-I\"/Users/davis/Library/R/arm64/4.4/library/Rcpp/include\"` for Rcpp headers. 61 | - `-I\"/Users/davis/Library/R/arm64/4.4/library/testthat/include\"` for testthat headers related to Catch tests. 62 | 63 | Note that this is specific to a path on your machine and the R version you are currently working with. 64 | 65 | ## Future directions 66 | 67 | Some work could be spent in smoothing out the `cpp_source()` / knitr chunk experience. 68 | Our main focus and use cases were in R packages, so that usage is more tested. 69 | Because we don't typically use cpp11 in non package contexts those use cases may not be as nice. 70 | 71 | For similar reasons the matrix support might be somewhat lacking, as the majority of our use cases do not deal with numeric matrices. 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "make: Entering directory 'cpp11test/src'" 3 | @Rscript -e 'devtools::load_all("cpp11test")' 4 | @echo "make: Leaving directory 'cpp11test/src'" 5 | 6 | test: all 7 | @echo "make: Entering directory 'cpp11test/tests/testthat'" 8 | @Rscript -e 'devtools::test("cpp11test")' 9 | @echo "make: Leaving directory 'cpp11test/tests/testthat'" 10 | 11 | clean: 12 | @Rscript -e 'devtools::clean_dll()' 13 | @Rscript -e 'devtools::clean_dll("cpp11test")' 14 | 15 | clang_format=`which clang-format` 16 | format: $(shell find . -name '*.hpp') $(shell find . -name '*.cpp') 17 | @${clang_format} -i $? 18 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(cpp_eval) 4 | export(cpp_function) 5 | export(cpp_register) 6 | export(cpp_source) 7 | export(cpp_vendor) 8 | -------------------------------------------------------------------------------- /R/coverage.R: -------------------------------------------------------------------------------- 1 | cpp11_coverage <- function(...) { 2 | old <- options(covr.filter_non_package = FALSE, covr.gcov_additional_paths = ".*/cpp11/") 3 | on.exit(options(old)) 4 | 5 | cpp11_coverage <- covr::package_coverage(".", ...) 6 | 7 | cpp11test_coverage <- covr::package_coverage("cpp11test", ...) 8 | 9 | cpp11test_coverage <- cpp11test_coverage[grepl("include/cpp11", covr::display_name(cpp11test_coverage))] 10 | attr(cpp11test_coverage, "package")$path <- sub("cpp11/include.*", "cpp11", covr::display_name(cpp11test_coverage)[[1]]) 11 | 12 | cov <- c(cpp11_coverage, cpp11test_coverage) 13 | attributes(cov) <- attributes(cpp11_coverage) 14 | 15 | cov 16 | } 17 | -------------------------------------------------------------------------------- /R/cpp11-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | -------------------------------------------------------------------------------- /R/knitr.R: -------------------------------------------------------------------------------- 1 | eng_cpp11 <- function(options) { 2 | if (options$eval) { 3 | cpp_source( 4 | code = options$code, 5 | env = knitr::knit_global(), 6 | clean = options$clean %||% TRUE, 7 | quiet = options$quiet %||% FALSE, 8 | cxx_std = options$cxx_std %||% Sys.getenv("CXX_STD", "CXX11") 9 | ) 10 | } 11 | 12 | # Change the engine to cpp so that code formatting works 13 | options$engine <- "cpp" 14 | knitr::engine_output(options, options$code, "") 15 | } 16 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | glue_collapse_data <- function(data, ..., sep = ", ", last = "") { 2 | res <- glue::glue_collapse(glue::glue_data(data, ...), sep = sep, last = last) 3 | if (length(res) == 0) { 4 | return("") 5 | } 6 | unclass(res) 7 | } 8 | 9 | `%||%` <- function(x, y) if (is.null(x)) y else x 10 | 11 | viapply <- function(x, f, ...) vapply(x, f, integer(1), ...) 12 | vcapply <- function(x, f, ...) vapply(x, f, character(1), ...) 13 | 14 | stop_unless_installed <- function(pkgs) { 15 | has_pkg <- logical(length(pkgs)) 16 | for (i in seq_along(pkgs)) { 17 | has_pkg[[i]] <- requireNamespace(pkgs[[i]], quietly = TRUE) 18 | } 19 | if (any(!has_pkg)) { 20 | msg <- sprintf( 21 | "The %s package(s) are required for this functionality", 22 | paste(pkgs[!has_pkg], collapse = ", ") 23 | ) 24 | 25 | if (is_interactive()) { 26 | ans <- readline(paste(c(msg, "Would you like to install them? (y/N) "), collapse = "\n")) 27 | if (tolower(ans) == "y") { 28 | utils::install.packages(pkgs[!has_pkg]) 29 | stop_unless_installed(pkgs) 30 | return() 31 | } 32 | } 33 | 34 | stop(msg, call. = FALSE) 35 | } 36 | } 37 | 38 | is_windows <- function() { 39 | .Platform$OS.type == "windows" 40 | } 41 | 42 | # This is basically the same as rlang::is_interactive(), which we can't really 43 | # use for stop_if_not_installed(), because rlang itself could be one of the 44 | # input pkgs. 45 | is_interactive <- function() { 46 | opt <- getOption("rlang_interactive", NULL) 47 | if (!is.null(opt)) { 48 | return(opt) 49 | } 50 | if (isTRUE(getOption("knitr.in.progress"))) { 51 | return(FALSE) 52 | } 53 | if (isTRUE(getOption("rstudio.notebook.executing"))) { 54 | return(FALSE) 55 | } 56 | if (identical(Sys.getenv("TESTTHAT"), "true")) { 57 | return(FALSE) 58 | } 59 | interactive() 60 | } 61 | -------------------------------------------------------------------------------- /R/vendor.R: -------------------------------------------------------------------------------- 1 | #' Vendor the cpp11 dependency 2 | #' 3 | #' Vendoring is the act of making your own copy of the 3rd party packages your 4 | #' project is using. It is often used in the go language community. 5 | #' 6 | #' This function vendors cpp11 into your package by copying the cpp11 7 | #' headers into the `inst/include` folder of your package and adding 8 | #' 'cpp11 version: XYZ' to the top of the files, where XYZ is the version of 9 | #' cpp11 currently installed on your machine. 10 | #' 11 | #' If you choose to vendor the headers you should _remove_ `LinkingTo: 12 | #' cpp11` from your DESCRIPTION. 13 | #' 14 | #' **Note**: vendoring places the responsibility of updating the code on 15 | #' **you**. Bugfixes and new features in cpp11 will not be available for your 16 | #' code until you run `cpp_vendor()` again. 17 | #' 18 | #' @inheritParams cpp_register 19 | #' @return The file path to the vendored code (invisibly). 20 | #' @export 21 | #' @examples 22 | #' # create a new directory 23 | #' dir <- tempfile() 24 | #' dir.create(dir) 25 | #' 26 | #' # vendor the cpp11 headers into the directory 27 | #' cpp_vendor(dir) 28 | #' 29 | #' list.files(file.path(dir, "inst", "include", "cpp11")) 30 | #' 31 | #' # cleanup 32 | #' unlink(dir, recursive = TRUE) 33 | cpp_vendor <- function(path = ".") { 34 | new <- file.path(path, "inst", "include", "cpp11") 35 | 36 | if (dir.exists(new)) { 37 | stop("'", new, "' already exists\n * run unlink('", new, "', recursive = TRUE)", call. = FALSE) 38 | } 39 | 40 | dir.create(new , recursive = TRUE, showWarnings = FALSE) 41 | 42 | current <- system.file("include", "cpp11", package = "cpp11") 43 | if (!nzchar(current)) { 44 | stop("cpp11 is not installed", call. = FALSE) 45 | } 46 | 47 | cpp11_version <- utils::packageVersion("cpp11") 48 | 49 | cpp11_header <- sprintf("// cpp11 version: %s\n// vendored on: %s", cpp11_version, Sys.Date()) 50 | 51 | files <- list.files(current, full.names = TRUE) 52 | 53 | writeLines( 54 | c(cpp11_header, readLines(system.file("include", "cpp11.hpp", package = "cpp11"))), 55 | file.path(dirname(new), "cpp11.hpp") 56 | ) 57 | 58 | for (f in files) { 59 | writeLines(c(cpp11_header, readLines(f)), file.path(new, basename(f))) 60 | } 61 | 62 | invisible(new) 63 | } 64 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # From https://github.com/r-lib/vctrs/blob/a518ead0b08be29beea287d11e17edc1017e16da/R/zzz.R#L3 2 | on_package_load <- function(pkg, expr) { 3 | if (isNamespaceLoaded(pkg)) { 4 | expr 5 | } else { 6 | thunk <- function(...) expr 7 | setHook(packageEvent(pkg, "onLoad"), thunk) 8 | } 9 | } 10 | 11 | # We need to set the cpp11 knitr engine when cpp11 is loaded. 12 | .onLoad <- function(libname, pkgname) { 13 | on_package_load("knitr", { 14 | knitr::knit_engines$set(cpp11 = eng_cpp11) 15 | }) 16 | } 17 | 18 | release_bullets <- function() { 19 | c( 20 | '`Sys.setenv("CPP11_EVAL" = "true"); devtools::submit_cran()`' 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp11 2 | 3 | 4 | [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) 5 | [![R-CMD-check](https://github.com/r-lib/cpp11/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/cpp11/actions/workflows/R-CMD-check.yaml) 6 | [![CRAN status](https://www.r-pkg.org/badges/version/cpp11)](https://CRAN.R-project.org/package=cpp11) 7 | [![Codecov test coverage](https://codecov.io/gh/r-lib/cpp11/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/cpp11?branch=main) 8 | 9 | 10 | cpp11 helps you to interact with R objects using C++ code. 11 | Its goals and syntax are similar to the excellent [Rcpp](https://cran.r-project.org/package=Rcpp) package. 12 | 13 | ## Using cpp11 in a package 14 | 15 | To add cpp11 to an existing package, put your C++ files in the `src/` directory and add the following to your DESCRIPTION file: 16 | 17 | ``` 18 | LinkingTo: cpp11 19 | ``` 20 | 21 | Then decorate C++ functions you want to expose to R with `[[cpp11::register]]`. 22 | *Note that this is a [C++11 attribute](https://en.cppreference.com/w/cpp/language/attributes), not a comment like is used in Rcpp.* 23 | 24 | cpp11 is a header only library with no hard dependencies and does not use a shared library, so it is straightforward and reliable to use in packages without fear of compile-time and run-time mismatches. 25 | 26 | Alternatively, you can [vendor](https://cpp11.r-lib.org/articles/motivations.html#vendoring) the current installed version of cpp11 headers into your package with `cpp11::vendor_cpp11()`. 27 | This ensures the headers will remain unchanged until you explicitly update them. 28 | 29 | ## Getting started 30 | 31 | See [vignette("cpp11")](https://cpp11.r-lib.org/articles/cpp11.html) to get started using cpp11 in your scripts, particularly if you are new to C++ programming. 32 | 33 | ## Getting help 34 | 35 | [Posit Community](https://forum.posit.co/) is the best place to ask for help using cpp11 or interfacing C++ with R. 36 | 37 | ## Motivations 38 | 39 | [Rcpp](https://cran.r-project.org/package=Rcpp) has been a widely successful project, however over the years a number of issues and additional C++ features have arisen. 40 | Adding these features to Rcpp would require a great deal of work, or in some cases would be impossible without severely breaking backwards compatibility. 41 | 42 | **cpp11** is a ground up rewrite of C++ bindings to R with different design trade-offs and features. 43 | 44 | Changes that motivated cpp11 include: 45 | 46 | - Enforcing [copy-on-write semantics](https://cpp11.r-lib.org/articles/motivations.html#copy-on-write-semantics). 47 | - Improving the [safety](https://cpp11.r-lib.org/articles/motivations.html#improve-safety) of using the R API from C++ code. 48 | - Supporting [ALTREP objects](https://cpp11.r-lib.org/articles/motivations.html#altrep-support). 49 | - Using [UTF-8 strings](https://cpp11.r-lib.org/articles/motivations.html#utf-8-everywhere) everywhere. 50 | - Applying newer [C++11 features](https://cpp11.r-lib.org/articles/motivations.html#c11-features). 51 | - Having a more straightforward, [simpler implementation](https://cpp11.r-lib.org/articles/motivations.html#simpler-implementation). 52 | - Faster [compilation time](https://cpp11.r-lib.org/articles/motivations.html#compilation-speed) with lower memory requirements. 53 | - Being *completely* [header only](https://cpp11.r-lib.org/articles/motivations.html#header-only) to avoid ABI issues. 54 | - Capable of [vendoring](https://cpp11.r-lib.org/articles/motivations.html#vendoring) if desired. 55 | - More robust [protection](https://cpp11.r-lib.org/articles/motivations.html#protection) using a much more efficient linked list data structure. 56 | - [Growing vectors](https://cpp11.r-lib.org/articles/motivations.html#growing-vectors) more efficiently. 57 | 58 | See [vignette("motivations")](https://cpp11.r-lib.org/articles/motivations.html) for full details on the motivations for writing cpp11. 59 | 60 | ## Conversion from Rcpp 61 | 62 | See [vignette("converting")](https://cpp11.r-lib.org/articles/converting.html) if you are already familiar with Rcpp or have an existing package that uses Rcpp and want to convert it to use cpp11. 63 | 64 | ## Learning More 65 | 66 | - [Welding R and C++](https://www.youtube.com/watch?v=_kq0N0FNIjA) - Presentation at SatRday Columbus [(slides)](https://speakerdeck.com/jimhester/cpp11-welding-r-and-c-plus-plus) 67 | 68 | 69 | ## Internals 70 | 71 | See [vignette("internals")](https://cpp11.r-lib.org/articles/internals.html) for details on the cpp11 implementation or if you would like to contribute to cpp11. 72 | 73 | ## Code of Conduct 74 | 75 | Please note that the cpp11 project is released with a [Contributor Code of Conduct](https://cpp11.r-lib.org/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 76 | 77 | ## Thanks 78 | 79 | cpp11 would not exist without Rcpp. 80 | 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. 81 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://cpp11.r-lib.org 2 | 3 | authors: 4 | "Jim Hester": 5 | href: http://jimhester.com 6 | 7 | template: 8 | package: tidytemplate 9 | bootstrap: 5 10 | 11 | includes: 12 | in_header: | 13 | 14 | 15 | development: 16 | mode: auto 17 | 18 | reference: 19 | - title: Registering C++ functions 20 | contents: 21 | - cpp_register 22 | - title: Compiling C++ code interactively 23 | contents: 24 | - cpp_source 25 | - title: Vendoring cpp11 26 | contents: 27 | - cpp_vendor 28 | 29 | navbar: 30 | type: default 31 | left: 32 | - text: Home 33 | href: index.html 34 | - text: Get started 35 | href: articles/cpp11.html 36 | - text: Motivations 37 | href: articles/motivations.html 38 | - text: Converting 39 | href: articles/converting.html 40 | - text: FAQ 41 | href: articles/FAQ.html 42 | - text: Internals 43 | href: articles/internals.html 44 | - text: Reference 45 | href: reference/index.html 46 | - text: News 47 | menu: 48 | - text: "Change log" 49 | href: news/index.html 50 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cpp11.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /cpp11test/.here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lib/cpp11/05c888b0c6f49e7b252b79b028e4719e6d5b299d/cpp11test/.here -------------------------------------------------------------------------------- /cpp11test/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: cpp11test 2 | Title: A test suite and benchmark code for 'cpp11' 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | c(person(given = "Jim", 6 | family = "Hester", 7 | role = c("aut", "cre"), 8 | email = "james.f.hester@gmail.com", 9 | comment = c(ORCID = "0000-0002-2739-7082")), 10 | person(given = "RStudio", 11 | role = c("cph", "fnd"))) 12 | Description: Provides a test suite and benchmarking code for the 'cpp11' package. 13 | License: MIT + file LICENSE 14 | Encoding: UTF-8 15 | LinkingTo: Rcpp, cpp11, testthat 16 | Suggests: 17 | covr, 18 | testthat, 19 | pkgload, 20 | xml2 21 | LazyData: true 22 | Roxygen: list(markdown = TRUE) 23 | RoxygenNote: 7.1.1 24 | -------------------------------------------------------------------------------- /cpp11test/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(run_tests) 4 | exportPattern("_$") 5 | importFrom(Rcpp,sourceCpp) 6 | useDynLib(cpp11test, .registration = TRUE) 7 | -------------------------------------------------------------------------------- /cpp11test/R/catch-routine-registration.R: -------------------------------------------------------------------------------- 1 | # This dummy function definition is included with the package to ensure that 2 | # 'tools::package_native_routine_registration_skeleton()' generates the required 3 | # registration info for the 'run_testthat_tests' symbol. 4 | (function() { 5 | .Call("run_testthat_tests", TRUE) 6 | }) 7 | -------------------------------------------------------------------------------- /cpp11test/R/cpp11.R: -------------------------------------------------------------------------------- 1 | # Generated by cpp11: do not edit by hand 2 | 3 | cpp11_add_vec_for_ <- function(x, num) { 4 | .Call(`_cpp11test_cpp11_add_vec_for_`, x, num) 5 | } 6 | 7 | data_frame_ <- function() { 8 | .Call(`_cpp11test_data_frame_`) 9 | } 10 | 11 | my_stop_n1fmt <- function(mystring) { 12 | invisible(.Call(`_cpp11test_my_stop_n1fmt`, mystring)) 13 | } 14 | 15 | my_stop_n2fmt <- function(mystring, myarg) { 16 | invisible(.Call(`_cpp11test_my_stop_n2fmt`, mystring, myarg)) 17 | } 18 | 19 | my_warning_n1fmt <- function(mystring) { 20 | invisible(.Call(`_cpp11test_my_warning_n1fmt`, mystring)) 21 | } 22 | 23 | my_warning_n2fmt <- function(mystring, myarg) { 24 | invisible(.Call(`_cpp11test_my_warning_n2fmt`, mystring, myarg)) 25 | } 26 | 27 | my_message_n1fmt <- function(mystring) { 28 | invisible(.Call(`_cpp11test_my_message_n1fmt`, mystring)) 29 | } 30 | 31 | my_message_n2fmt <- function(mystring, myarg) { 32 | invisible(.Call(`_cpp11test_my_message_n2fmt`, mystring, myarg)) 33 | } 34 | 35 | my_stop <- function(mystring, myarg) { 36 | invisible(.Call(`_cpp11test_my_stop`, mystring, myarg)) 37 | } 38 | 39 | my_stop_n1 <- function(mystring) { 40 | invisible(.Call(`_cpp11test_my_stop_n1`, mystring)) 41 | } 42 | 43 | my_warning <- function(mystring, myarg) { 44 | invisible(.Call(`_cpp11test_my_warning`, mystring, myarg)) 45 | } 46 | 47 | my_warning_n1 <- function(mystring) { 48 | invisible(.Call(`_cpp11test_my_warning_n1`, mystring)) 49 | } 50 | 51 | my_message <- function(mystring, myarg) { 52 | invisible(.Call(`_cpp11test_my_message`, mystring, myarg)) 53 | } 54 | 55 | my_message_n1 <- function(mystring) { 56 | invisible(.Call(`_cpp11test_my_message_n1`, mystring)) 57 | } 58 | 59 | remove_altrep <- function(x) { 60 | .Call(`_cpp11test_remove_altrep`, x) 61 | } 62 | 63 | upper_bound <- function(x, breaks) { 64 | .Call(`_cpp11test_upper_bound`, x, breaks) 65 | } 66 | 67 | findInterval2 <- function(x, breaks) { 68 | .Call(`_cpp11test_findInterval2`, x, breaks) 69 | } 70 | 71 | findInterval2_5 <- function(x, breaks) { 72 | .Call(`_cpp11test_findInterval2_5`, x, breaks) 73 | } 74 | 75 | findInterval3 <- function(x, breaks) { 76 | .Call(`_cpp11test_findInterval3`, x, breaks) 77 | } 78 | 79 | findInterval4 <- function(x, breaks) { 80 | .Call(`_cpp11test_findInterval4`, x, breaks) 81 | } 82 | 83 | grow_ <- function(n) { 84 | .Call(`_cpp11test_grow_`, n) 85 | } 86 | 87 | cpp11_insert_ <- function(num_sxp) { 88 | .Call(`_cpp11test_cpp11_insert_`, num_sxp) 89 | } 90 | 91 | gibbs_cpp <- function(N, thin) { 92 | .Call(`_cpp11test_gibbs_cpp`, N, thin) 93 | } 94 | 95 | gibbs_cpp2 <- function(N, thin) { 96 | .Call(`_cpp11test_gibbs_cpp2`, N, thin) 97 | } 98 | 99 | gibbs_rcpp <- function(N, thin) { 100 | .Call(`_cpp11test_gibbs_rcpp`, N, thin) 101 | } 102 | 103 | gibbs_rcpp2 <- function(N, thin) { 104 | .Call(`_cpp11test_gibbs_rcpp2`, N, thin) 105 | } 106 | 107 | row_sums <- function(x) { 108 | .Call(`_cpp11test_row_sums`, x) 109 | } 110 | 111 | col_sums <- function(x) { 112 | .Call(`_cpp11test_col_sums`, x) 113 | } 114 | 115 | protect_one_ <- function(x, n) { 116 | invisible(.Call(`_cpp11test_protect_one_`, x, n)) 117 | } 118 | 119 | protect_one_sexp_ <- function(x, n) { 120 | invisible(.Call(`_cpp11test_protect_one_sexp_`, x, n)) 121 | } 122 | 123 | protect_one_cpp11_ <- function(x, n) { 124 | invisible(.Call(`_cpp11test_protect_one_cpp11_`, x, n)) 125 | } 126 | 127 | protect_one_preserve_ <- function(x, n) { 128 | invisible(.Call(`_cpp11test_protect_one_preserve_`, x, n)) 129 | } 130 | 131 | protect_many_ <- function(n) { 132 | invisible(.Call(`_cpp11test_protect_many_`, n)) 133 | } 134 | 135 | protect_many_cpp11_ <- function(n) { 136 | invisible(.Call(`_cpp11test_protect_many_cpp11_`, n)) 137 | } 138 | 139 | protect_many_sexp_ <- function(n) { 140 | invisible(.Call(`_cpp11test_protect_many_sexp_`, n)) 141 | } 142 | 143 | protect_many_preserve_ <- function(n) { 144 | invisible(.Call(`_cpp11test_protect_many_preserve_`, n)) 145 | } 146 | 147 | protect_many_rcpp_ <- function(n) { 148 | invisible(.Call(`_cpp11test_protect_many_rcpp_`, n)) 149 | } 150 | 151 | cpp11_release_ <- function(n) { 152 | invisible(.Call(`_cpp11test_cpp11_release_`, n)) 153 | } 154 | 155 | rcpp_release_ <- function(n) { 156 | invisible(.Call(`_cpp11test_rcpp_release_`, n)) 157 | } 158 | 159 | cpp11_safe_ <- function(x_sxp) { 160 | .Call(`_cpp11test_cpp11_safe_`, x_sxp) 161 | } 162 | 163 | string_proxy_assignment_ <- function() { 164 | .Call(`_cpp11test_string_proxy_assignment_`) 165 | } 166 | 167 | string_push_back_ <- function() { 168 | .Call(`_cpp11test_string_push_back_`) 169 | } 170 | 171 | sum_dbl_for_ <- function(x) { 172 | .Call(`_cpp11test_sum_dbl_for_`, x) 173 | } 174 | 175 | sum_dbl_for2_ <- function(x_sxp) { 176 | .Call(`_cpp11test_sum_dbl_for2_`, x_sxp) 177 | } 178 | 179 | sum_dbl_for3_ <- function(x_sxp) { 180 | .Call(`_cpp11test_sum_dbl_for3_`, x_sxp) 181 | } 182 | 183 | sum_dbl_foreach_ <- function(x) { 184 | .Call(`_cpp11test_sum_dbl_foreach_`, x) 185 | } 186 | 187 | sum_dbl_foreach2_ <- function(x_sxp) { 188 | .Call(`_cpp11test_sum_dbl_foreach2_`, x_sxp) 189 | } 190 | 191 | sum_dbl_accumulate_ <- function(x) { 192 | .Call(`_cpp11test_sum_dbl_accumulate_`, x) 193 | } 194 | 195 | sum_dbl_accumulate2_ <- function(x_sxp) { 196 | .Call(`_cpp11test_sum_dbl_accumulate2_`, x_sxp) 197 | } 198 | 199 | sum_int_for_ <- function(x) { 200 | .Call(`_cpp11test_sum_int_for_`, x) 201 | } 202 | 203 | sum_int_for2_ <- function(x_) { 204 | .Call(`_cpp11test_sum_int_for2_`, x_) 205 | } 206 | 207 | sum_int_foreach_ <- function(x) { 208 | .Call(`_cpp11test_sum_int_foreach_`, x) 209 | } 210 | 211 | sum_int_accumulate_ <- function(x) { 212 | .Call(`_cpp11test_sum_int_accumulate_`, x) 213 | } 214 | 215 | rcpp_sum_dbl_for_ <- function(x_sxp) { 216 | .Call(`_cpp11test_rcpp_sum_dbl_for_`, x_sxp) 217 | } 218 | 219 | rcpp_sum_int_for_ <- function(x_sxp) { 220 | .Call(`_cpp11test_rcpp_sum_int_for_`, x_sxp) 221 | } 222 | 223 | rcpp_sum_dbl_foreach_ <- function(x_sxp) { 224 | .Call(`_cpp11test_rcpp_sum_dbl_foreach_`, x_sxp) 225 | } 226 | 227 | rcpp_sum_dbl_accumulate_ <- function(x_sxp) { 228 | .Call(`_cpp11test_rcpp_sum_dbl_accumulate_`, x_sxp) 229 | } 230 | 231 | rcpp_grow_ <- function(n_sxp) { 232 | .Call(`_cpp11test_rcpp_grow_`, n_sxp) 233 | } 234 | 235 | rcpp_push_and_truncate_ <- function(size_sxp) { 236 | .Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp) 237 | } 238 | 239 | test_destruction_inner <- function() { 240 | invisible(.Call(`_cpp11test_test_destruction_inner`)) 241 | } 242 | 243 | test_destruction_outer <- function() { 244 | invisible(.Call(`_cpp11test_test_destruction_outer`)) 245 | } 246 | 247 | cpp11_push_and_truncate_ <- function(size_sexp) { 248 | .Call(`_cpp11test_cpp11_push_and_truncate_`, size_sexp) 249 | } 250 | -------------------------------------------------------------------------------- /cpp11test/R/cpp11test-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @exportPattern "_$" 3 | #' @importFrom Rcpp sourceCpp 4 | "_PACKAGE" 5 | 6 | # The following block is used by usethis to automatically manage 7 | # roxygen namespace tags. Modify with care! 8 | ## usethis namespace: start 9 | #' @useDynLib cpp11test, .registration = TRUE 10 | ## usethis namespace: end 11 | NULL 12 | -------------------------------------------------------------------------------- /cpp11test/R/should_run_benchmarks.R: -------------------------------------------------------------------------------- 1 | should_run_benchmarks <- function() { 2 | isTRUE(as.logical(Sys.getenv("CPP11TEST_SHOULD_RUN_BENCHMARKS", "false"))) 3 | } 4 | -------------------------------------------------------------------------------- /cpp11test/R/test.R: -------------------------------------------------------------------------------- 1 | #' Run the cpp11 tests 2 | #' @inheritParams testthat::test_check 3 | #' @export 4 | run_tests <- function(reporter = testthat::default_reporter()) { 5 | if (interactive()) { 6 | if (pkgload::is_dev_package("cpp11")) { 7 | # load cpp11 8 | pkgload::load_all(system.file(".", package = "cpp11")) 9 | 10 | # load cpp11test 11 | pkgload::load_all(system.file("cpp11test", package = "cpp11")) 12 | } else { 13 | # load cpp11 14 | pkgload::load_all("..") 15 | 16 | # load cpp11test 17 | pkgload::load_all() 18 | } 19 | } 20 | 21 | old <- getwd() 22 | on.exit(setwd(old)) 23 | setwd(system.file("tests", package = "cpp11test")) 24 | 25 | library(testthat) 26 | test_check("cpp11test", reporter = reporter) 27 | } 28 | -------------------------------------------------------------------------------- /cpp11test/bench/grow.R: -------------------------------------------------------------------------------- 1 | pkgload::load_all("cpp11test") 2 | 3 | bench::press(len = 10 ^ (0:7), 4 | { 5 | bench::mark( 6 | grow_(len) 7 | ) 8 | } 9 | ) 10 | -------------------------------------------------------------------------------- /cpp11test/bench/insert.R: -------------------------------------------------------------------------------- 1 | pkgload::load_all("cpp11test") 2 | 3 | bench::press( 4 | len = as.integer(10^(0:4)), 5 | { 6 | bench::mark( 7 | cpp11_insert_(len) 8 | ) 9 | } 10 | ) 11 | -------------------------------------------------------------------------------- /cpp11test/bench/protect.R: -------------------------------------------------------------------------------- 1 | pkgload::load_all("cpp11test") 2 | 3 | bench::press(n = 10000, 4 | bench::mark( 5 | protect_one_(1:10, n), 6 | protect_one_cpp11_(1:10, n), 7 | protect_one_sexp_(1:10, n), 8 | protect_one_preserve_(1:10, n) 9 | ) 10 | ) 11 | 12 | bench::press(n = 10000, 13 | bench::mark( 14 | protect_many_(n), 15 | protect_many_cpp11_(n), 16 | protect_many_sexp_(n), 17 | protect_many_preserve_(n), 18 | protect_many_rcpp_(n) 19 | ) 20 | ) 21 | -------------------------------------------------------------------------------- /cpp11test/bench/sum.R: -------------------------------------------------------------------------------- 1 | pkgload::load_all("cpp11test") 2 | 3 | cases <- expand.grid( 4 | len = 3e6, 5 | vector = c("normal", "altrep"), 6 | method = c("for", "foreach", "accumulate"), 7 | stringsAsFactors = FALSE 8 | ) 9 | 10 | # Add special case 11 | cases <- do.call(rbind, 12 | list( 13 | list(len = 3e6, vector = "normal", method = "for2"), 14 | list(len = 3e6, vector = "normal", method = "foreach2"), 15 | list(len = 3e6, vector = "altrep", method = "foreach2"), 16 | cases) 17 | ) 18 | 19 | bench::press( 20 | .grid = cases, 21 | { 22 | library(cpp11) 23 | seq_real <- function(x) as.numeric(seq_len(x)) 24 | funs <- c("normal" = rnorm, "altrep" = seq_real) 25 | x <- funs[[vector]](len) 26 | fun_name <- get(sprintf("sum_dbl_%s_", method)) 27 | bench::mark( 28 | fun_name(x) 29 | ) 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /cpp11test/bench/truncate.R: -------------------------------------------------------------------------------- 1 | pkgload::load_all("cpp11test") 2 | 3 | bench::press(len = as.integer(10 ^ (0:6)), 4 | { 5 | bench::mark( 6 | cpp11 = cpp11_push_and_truncate_(len), 7 | rcpp = rcpp_push_and_truncate_(len), 8 | check = FALSE, 9 | min_iterations = 1000 10 | ) 11 | } 12 | )[c("expression", "len", "min", "mem_alloc", "n_itr", "n_gc")] 13 | 14 | # Longer benchmark, lots of gc 15 | len <- as.integer(10 ^ 7) 16 | bench::mark( 17 | cpp11 = cpp11_push_and_truncate_(len), 18 | rcpp = rcpp_push_and_truncate_(len), 19 | min_iterations = 200 20 | )[c("expression", "min", "mem_alloc", "n_itr", "n_gc")] 21 | -------------------------------------------------------------------------------- /cpp11test/man/cpp11test-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cpp11test-package.R 3 | \docType{package} 4 | \name{cpp11test-package} 5 | \alias{cpp11test} 6 | \alias{cpp11test-package} 7 | \title{cpp11test: A test suite and benchmark code for 'cpp11'} 8 | \description{ 9 | Provides a test suite and benchmarking code for the 'cpp11' package. 10 | } 11 | \author{ 12 | \strong{Maintainer}: Jim Hester \email{james.f.hester@gmail.com} (\href{https://orcid.org/0000-0002-2739-7082}{ORCID}) 13 | 14 | Other contributors: 15 | \itemize{ 16 | \item RStudio [copyright holder, funder] 17 | } 18 | 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /cpp11test/man/run_tests.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/test.R 3 | \name{run_tests} 4 | \alias{run_tests} 5 | \title{Run the cpp11 tests} 6 | \usage{ 7 | run_tests(reporter = testthat::default_reporter()) 8 | } 9 | \arguments{ 10 | \item{reporter}{Reporter to use to summarise output. Can be supplied 11 | as a string (e.g. "summary") or as an R6 object 12 | (e.g. \code{SummaryReporter$new()}). 13 | 14 | See \link[testthat]{Reporter} for more details and a list of built-in reporters.} 15 | } 16 | \description{ 17 | Run the cpp11 tests 18 | } 19 | -------------------------------------------------------------------------------- /cpp11test/src/add.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | 3 | [[cpp11::register]] SEXP cpp11_add_vec_for_(cpp11::writable::doubles x, double num) { 4 | R_xlen_t n = x.size(); 5 | for (R_xlen_t i = 0; i < n; ++i) { 6 | double cur = x[i]; 7 | x[i] = cur + num; 8 | } 9 | 10 | return x; 11 | } 12 | -------------------------------------------------------------------------------- /cpp11test/src/data_frame.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/integers.hpp" 2 | #include "cpp11/list.hpp" 3 | #include "cpp11/strings.hpp" 4 | 5 | [[cpp11::register]] SEXP data_frame_() { 6 | using namespace cpp11::literals; 7 | cpp11::writable::list out({ 8 | "nums"_nm = {1, 2, 3}, 9 | "letters"_nm = {"x", "y", "z"}, 10 | }); 11 | 12 | out.attr("class") = "data.frame"; 13 | out.attr("row.names") = {NA_INTEGER, -3}; 14 | 15 | return out; 16 | } 17 | -------------------------------------------------------------------------------- /cpp11test/src/errors.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/function.hpp" 2 | #include "cpp11/protect.hpp" 3 | using namespace cpp11; 4 | 5 | [[cpp11::register]] void my_stop_n1fmt(std::string mystring) { cpp11::stop(mystring); } 6 | [[cpp11::register]] void my_stop_n2fmt(std::string mystring, std::string myarg) { 7 | cpp11::stop(mystring, myarg.c_str()); 8 | } 9 | [[cpp11::register]] void my_warning_n1fmt(std::string mystring) { 10 | cpp11::warning(mystring); 11 | } 12 | [[cpp11::register]] void my_warning_n2fmt(std::string mystring, std::string myarg) { 13 | cpp11::warning(mystring, myarg.c_str()); 14 | } 15 | [[cpp11::register]] void my_message_n1fmt(std::string mystring) { 16 | cpp11::message(mystring); 17 | } 18 | [[cpp11::register]] void my_message_n2fmt(std::string mystring, std::string myarg) { 19 | cpp11::message(mystring, myarg.c_str()); 20 | } 21 | -------------------------------------------------------------------------------- /cpp11test/src/errors_fmt.cpp: -------------------------------------------------------------------------------- 1 | #define CPP11_USE_FMT 2 | #include "cpp11/function.hpp" 3 | #include "cpp11/protect.hpp" 4 | using namespace cpp11; 5 | 6 | [[cpp11::register]] void my_stop(std::string mystring, int myarg) { 7 | cpp11::stop(mystring, myarg); 8 | } 9 | [[cpp11::register]] void my_stop_n1(std::string mystring) { cpp11::stop(mystring); } 10 | [[cpp11::register]] void my_warning(std::string mystring, std::string myarg) { 11 | cpp11::warning(mystring, myarg); 12 | } 13 | [[cpp11::register]] void my_warning_n1(std::string mystring) { cpp11::warning(mystring); } 14 | [[cpp11::register]] void my_message(std::string mystring, std::string myarg) { 15 | cpp11::message(mystring, myarg); 16 | } 17 | [[cpp11::register]] void my_message_n1(std::string mystring) { cpp11::message(mystring); } 18 | -------------------------------------------------------------------------------- /cpp11test/src/find-intervals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11.hpp" 3 | using namespace cpp11; 4 | 5 | #include 6 | #include 7 | using namespace Rcpp; 8 | 9 | [[cpp11::register]] SEXP remove_altrep(SEXP x) { 10 | SEXP out = PROTECT(Rf_allocVector(REALSXP, Rf_xlength(x))); 11 | memcpy(REAL(out), REAL(x), Rf_xlength(x)); 12 | 13 | UNPROTECT(1); 14 | return out; 15 | } 16 | 17 | [[cpp11::register]] double upper_bound(doubles x, doubles breaks) { 18 | auto pos = std::upper_bound(breaks.begin(), breaks.end(), x[0]); 19 | return std::distance(breaks.begin(), pos); 20 | } 21 | 22 | [[cpp11::register]] integers findInterval2(doubles x, doubles breaks) { 23 | writable::integers out(x.size()); 24 | auto out_it = out.begin(); 25 | 26 | for (auto&& val : x) { 27 | auto pos = std::upper_bound(breaks.begin(), breaks.end(), val); 28 | *out_it = std::distance(breaks.begin(), pos); 29 | ++out_it; 30 | } 31 | return out; 32 | } 33 | [[cpp11::register]] integers findInterval2_5(doubles x, doubles breaks) { 34 | writable::integers out(x.size()); 35 | auto out_it = out.begin(); 36 | auto bb = breaks.begin(); 37 | auto be = breaks.end(); 38 | 39 | for (auto&& val : x) { 40 | auto pos = std::upper_bound(bb, be, val); 41 | *out_it = std::distance(bb, pos); 42 | ++out_it; 43 | } 44 | return out; 45 | } 46 | 47 | // This version avoids the overhead of the cpp11 iterator types 48 | [[cpp11::register]] integers findInterval3(doubles x, doubles breaks) { 49 | writable::integers out(x.size()); 50 | auto out_it = out.begin(); 51 | auto b = REAL(breaks); 52 | auto e = REAL(breaks) + Rf_xlength(breaks); 53 | double* pos; 54 | 55 | for (auto&& val : x) { 56 | pos = std::upper_bound(b, e, val); 57 | *out_it = std::distance(b, pos); 58 | ++out_it; 59 | } 60 | return out; 61 | } 62 | 63 | [[cpp11::register]] IntegerVector findInterval4(NumericVector x, NumericVector breaks) { 64 | IntegerVector out(x.size()); 65 | 66 | NumericVector::iterator it, it_end, pos, b, e; 67 | IntegerVector::iterator out_it; 68 | it_end = x.end(); 69 | b = breaks.begin(); 70 | e = breaks.end(); 71 | 72 | for (it = x.begin(), out_it = out.begin(); it != it_end; ++it, ++out_it) { 73 | pos = std::upper_bound(b, e, *it); 74 | *out_it = std::distance(b, pos); 75 | } 76 | 77 | return out; 78 | } 79 | 80 | /* R code to benchmark these implementations 81 | res <- bench::press( 82 | n1 = 10^seq(1, 3), 83 | n2 = 10^seq(1, 5), 84 | { 85 | x <- c(-n1, seq(-2, 2, length = n1 + 1), n1) 86 | y <- sort(round(stats::rt(n2, df = 2), 2)) 87 | bench::mark( 88 | findInterval(x, y), 89 | findInterval2(x, y), 90 | findInterval2_5(x, y), 91 | findInterval3(x, y), 92 | findInterval4(x, y) 93 | ) 94 | } 95 | ) 96 | */ 97 | -------------------------------------------------------------------------------- /cpp11test/src/grow.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | 3 | [[cpp11::register]] cpp11::writable::doubles grow_(R_xlen_t n) { 4 | cpp11::writable::doubles x; 5 | R_xlen_t i = 0; 6 | while (i < n) { 7 | x.push_back(i++); 8 | } 9 | 10 | return x; 11 | } 12 | -------------------------------------------------------------------------------- /cpp11test/src/insert.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | 3 | [[cpp11::register]] SEXP cpp11_insert_(SEXP num_sxp) { 4 | R_xlen_t num = INTEGER(num_sxp)[0]; 5 | 6 | R_xlen_t i = 0; 7 | cpp11::writable::doubles out; 8 | while (i < num) { 9 | out.insert(0, i++); 10 | } 11 | return SEXP(out); 12 | } 13 | -------------------------------------------------------------------------------- /cpp11test/src/matrix.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/matrix.hpp" 2 | #include "Rmath.h" 3 | #include "cpp11/doubles.hpp" 4 | using namespace cpp11; 5 | 6 | [[cpp11::register]] SEXP gibbs_cpp(int N, int thin) { 7 | cpp11::writable::doubles_matrix<> mat(N, 2); 8 | double x = 0, y = 0; 9 | for (int i = 0; i < N; i++) { 10 | for (int j = 0; j < thin; j++) { 11 | x = Rf_rgamma(3., 1. / double(y * y + 4)); 12 | y = Rf_rnorm(1. / (x + 1.), 1. / sqrt(2. * (x + 1.))); 13 | // REprintf("x: %f y: %f\n", x, y); 14 | } 15 | mat[i][0] = x; 16 | mat[i][1] = y; 17 | } 18 | return mat; 19 | } 20 | 21 | [[cpp11::register]] cpp11::doubles_matrix<> gibbs_cpp2(int N, int thin) { 22 | cpp11::writable::doubles_matrix<> mat(N, 2); 23 | double x = 0, y = 0; 24 | for (int i = 0; i < N; i++) { 25 | for (int j = 0; j < thin; j++) { 26 | x = Rf_rgamma(3., 1. / double(y * y + 4)); 27 | y = Rf_rnorm(1. / (x + 1.), 1. / sqrt(2. * (x + 1.))); 28 | } 29 | mat(i, 0) = x; 30 | mat(i, 1) = y; 31 | } 32 | return mat; 33 | } 34 | 35 | #include 36 | using namespace Rcpp; 37 | 38 | [[cpp11::register]] NumericMatrix gibbs_rcpp(int N, int thin) { 39 | NumericMatrix mat(N, 2); 40 | double x = 0, y = 0; 41 | 42 | for (int i = 0; i < N; i++) { 43 | for (int j = 0; j < thin; j++) { 44 | x = rgamma(1, 3, 1 / (y * y + 4))[0]; 45 | y = rnorm(1, 1 / (x + 1), 1 / sqrt(2 * (x + 1)))[0]; 46 | } 47 | mat(i, 0) = x; 48 | mat(i, 1) = y; 49 | } 50 | 51 | return (mat); 52 | } 53 | 54 | [[cpp11::register]] NumericMatrix gibbs_rcpp2(int N, int thin) { 55 | NumericMatrix mat(N, 2); 56 | double x = 0, y = 0; 57 | 58 | for (int i = 0; i < N; i++) { 59 | for (int j = 0; j < thin; j++) { 60 | x = Rf_rgamma(3., 1. / (y * y + 4)); 61 | y = Rf_rnorm(1. / (x + 1), 1 / sqrt(2 * (x + 1))); 62 | } 63 | mat(i, 0) = x; 64 | mat(i, 1) = y; 65 | } 66 | 67 | return (mat); 68 | } 69 | 70 | [[cpp11::register]] cpp11::doubles row_sums(cpp11::doubles_matrix x) { 71 | cpp11::writable::doubles sums(x.nrow()); 72 | 73 | int i = 0; 74 | for (auto row : x) { 75 | sums[i] = 0.; 76 | for (auto&& val : row) { 77 | if (cpp11::is_na(val)) { 78 | sums[i] = NA_REAL; 79 | break; 80 | } 81 | sums[i] += val; 82 | } 83 | ++i; 84 | } 85 | 86 | return sums; 87 | } 88 | 89 | [[cpp11::register]] cpp11::doubles col_sums(cpp11::doubles_matrix x) { 90 | cpp11::writable::doubles sums(x.ncol()); 91 | 92 | int i = 0; 93 | for (auto col : x) { 94 | sums[i] = 0.; 95 | for (auto&& val : col) { 96 | if (cpp11::is_na(val)) { 97 | sums[i] = NA_REAL; 98 | break; 99 | } 100 | sums[i] += val; 101 | } 102 | ++i; 103 | } 104 | 105 | return sums; 106 | } 107 | -------------------------------------------------------------------------------- /cpp11test/src/protect.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Rcpp.h" 5 | 6 | [[cpp11::register]] void protect_one_(SEXP x, int n) { 7 | for (R_xlen_t i = 0; i < n; ++i) { 8 | PROTECT(x); 9 | UNPROTECT(1); 10 | } 11 | } 12 | 13 | [[cpp11::register]] void protect_one_sexp_(SEXP x, int n) { 14 | for (R_xlen_t i = 0; i < n; ++i) { 15 | cpp11::sexp y(x); 16 | } 17 | } 18 | 19 | [[cpp11::register]] void protect_one_cpp11_(SEXP x, int n) { 20 | for (R_xlen_t i = 0; i < n; ++i) { 21 | SEXP p = cpp11::detail::store::insert(x); 22 | cpp11::detail::store::release(p); 23 | } 24 | } 25 | 26 | [[cpp11::register]] void protect_one_preserve_(SEXP x, int n) { 27 | for (R_xlen_t i = 0; i < n; ++i) { 28 | R_PreserveObject(x); 29 | R_ReleaseObject(x); 30 | } 31 | } 32 | 33 | // The internal protections here are actually uneeded, but it is a useful way to benchmark 34 | // them 35 | 36 | [[cpp11::register]] void protect_many_(int n) { 37 | #ifdef CPP11_BENCH 38 | std::vector res; 39 | for (R_xlen_t i = 0; i < n; ++i) { 40 | res.push_back(PROTECT(Rf_ScalarInteger(n))); 41 | } 42 | 43 | for (R_xlen_t i = n - 1; i >= 0; --i) { 44 | SEXP x = res[i]; 45 | UNPROTECT(1); 46 | res.pop_back(); 47 | } 48 | #endif 49 | } 50 | 51 | [[cpp11::register]] void protect_many_cpp11_(int n) { 52 | std::vector res; 53 | for (R_xlen_t i = 0; i < n; ++i) { 54 | res.push_back(cpp11::detail::store::insert(Rf_ScalarInteger(n))); 55 | } 56 | 57 | for (R_xlen_t i = n - 1; i >= 0; --i) { 58 | SEXP x = res[i]; 59 | cpp11::detail::store::release(x); 60 | res.pop_back(); 61 | } 62 | } 63 | 64 | [[cpp11::register]] void protect_many_sexp_(int n) { 65 | std::vector res; 66 | for (R_xlen_t i = 0; i < n; ++i) { 67 | res.push_back(Rf_ScalarInteger(n)); 68 | } 69 | 70 | for (R_xlen_t i = n - 1; i >= 0; --i) { 71 | res.pop_back(); 72 | } 73 | } 74 | 75 | [[cpp11::register]] void protect_many_preserve_(int n) { 76 | std::vector res; 77 | for (R_xlen_t i = 0; i < n; ++i) { 78 | SEXP x = Rf_ScalarInteger(n); 79 | R_PreserveObject(x); 80 | res.push_back(x); 81 | } 82 | 83 | for (R_xlen_t i = n - 1; i >= 0; --i) { 84 | SEXP x = res[i]; 85 | R_ReleaseObject(x); 86 | res.pop_back(); 87 | } 88 | } 89 | 90 | [[cpp11::register]] void protect_many_rcpp_(int n) { 91 | #ifdef CPP11_BENCH 92 | std::vector res; 93 | for (R_xlen_t i = 0; i < n; ++i) { 94 | res.push_back(Rcpp::RObject(Rf_ScalarInteger(n))); 95 | } 96 | 97 | for (R_xlen_t i = n - 1; i >= 0; --i) { 98 | SEXP x = res[i]; 99 | res.pop_back(); 100 | } 101 | #endif 102 | } 103 | -------------------------------------------------------------------------------- /cpp11test/src/release.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11/sexp.hpp" 3 | 4 | #include "Rcpp.h" 5 | 6 | [[cpp11::register]] void cpp11_release_(int n) { 7 | std::vector x; 8 | int count = 0; 9 | while (count < n) { 10 | x.push_back(Rf_ScalarInteger(count)); 11 | ++count; 12 | } 13 | count = 0; 14 | while (count < n) { 15 | x.pop_back(); 16 | ++count; 17 | } 18 | } 19 | 20 | [[cpp11::register]] void rcpp_release_(int n) { 21 | #ifdef CPP11_BENCH 22 | std::vector x; 23 | int count = 0; 24 | while (count < n) { 25 | x.push_back(Rcpp::RObject(Rf_ScalarInteger(count))); 26 | ++count; 27 | } 28 | count = 0; 29 | while (count < n) { 30 | x.pop_back(); 31 | ++count; 32 | } 33 | #endif 34 | } 35 | -------------------------------------------------------------------------------- /cpp11test/src/safe.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11/doubles.hpp" 3 | #include "cpp11/protect.hpp" 4 | 5 | [[cpp11::register]] SEXP cpp11_safe_(SEXP x_sxp) { 6 | SEXP err = R_NilValue; 7 | const size_t ERROR_SIZE = 2048; 8 | char buf[ERROR_SIZE] = ""; 9 | 10 | try { 11 | const cpp11::doubles x(x_sxp); 12 | // Rf_error("R error"); // This will not call dtors 13 | // throw std::runtime_error("C++ error"); 14 | // cpp11::unwind_protect([&]() { Rf_error("R error"); }); 15 | SEXP out = cpp11::unwind_protect([&] { return Rf_allocVector(REALSXP, 1); }); 16 | 17 | return out; 18 | } 19 | 20 | catch (cpp11::unwind_exception& e) { 21 | err = e.token; 22 | } catch (std::exception& e) { 23 | strncpy(buf, e.what(), ERROR_SIZE - 1); 24 | } 25 | 26 | if (buf[0] != '\0') { 27 | Rf_error("%s", buf); 28 | } else if (err != R_NilValue) { 29 | R_ContinueUnwind(err); 30 | } 31 | 32 | return R_NilValue; 33 | } 34 | -------------------------------------------------------------------------------- /cpp11test/src/strings.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/strings.hpp" 2 | 3 | // Test benchmark for string proxy assignment performance. 4 | // We don't unwind_protect() before each `SET_STRING_ELT()` call, 5 | // as that kills performance. 6 | [[cpp11::register]] cpp11::writable::strings string_proxy_assignment_() { 7 | R_xlen_t size = 100000; 8 | 9 | cpp11::writable::strings x(size); 10 | 11 | cpp11::r_string elt(NA_STRING); 12 | 13 | for (R_xlen_t i = 0; i < size; ++i) { 14 | x[i] = elt; 15 | } 16 | 17 | return x; 18 | } 19 | 20 | // Test benchmark for string push back performance. 21 | // We don't unwind_protect() before each `SET_STRING_ELT()` call, 22 | // as that kills performance. 23 | [[cpp11::register]] cpp11::writable::strings string_push_back_() { 24 | R_xlen_t size = 100000; 25 | 26 | cpp11::writable::strings x; 27 | 28 | cpp11::r_string elt(NA_STRING); 29 | 30 | for (R_xlen_t i = 0; i < size; ++i) { 31 | x.push_back(elt); 32 | } 33 | 34 | return x; 35 | } 36 | -------------------------------------------------------------------------------- /cpp11test/src/sum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11/doubles.hpp" 3 | 4 | [[cpp11::register]] double sum_dbl_for_(cpp11::doubles x) { 5 | double sum = 0.; 6 | R_xlen_t n = x.size(); 7 | for (R_xlen_t i = 0; i < n; ++i) { 8 | sum += x[i]; 9 | } 10 | 11 | return sum; 12 | } 13 | 14 | [[cpp11::register]] double sum_dbl_for2_(SEXP x_sxp) { 15 | double sum = 0.; 16 | const cpp11::doubles x(x_sxp, false); 17 | R_xlen_t n = x.size(); 18 | for (R_xlen_t i = 0; i < n; ++i) { 19 | sum += x[i]; 20 | } 21 | 22 | return sum; 23 | } 24 | 25 | [[cpp11::register]] double sum_dbl_for3_(SEXP x_sxp) { 26 | double sum = 0.; 27 | const cpp11::writable::doubles x(x_sxp, false); 28 | R_xlen_t n = x.size(); 29 | for (R_xlen_t i = 0; i < n; ++i) { 30 | sum += x[i]; 31 | } 32 | 33 | return sum; 34 | } 35 | 36 | [[cpp11::register]] double sum_dbl_foreach_(cpp11::doubles x) { 37 | double sum = 0.; 38 | for (const auto&& val : x) { 39 | sum += val; 40 | } 41 | return sum; 42 | } 43 | 44 | [[cpp11::register]] double sum_dbl_foreach2_(SEXP x_sxp) { 45 | const cpp11::doubles x(x_sxp, false); 46 | double sum = 0.; 47 | for (const auto&& val : x) { 48 | sum += val; 49 | } 50 | return sum; 51 | } 52 | 53 | [[cpp11::register]] double sum_dbl_accumulate_(cpp11::doubles x) { 54 | return std::accumulate(x.cbegin(), x.cend(), 0.); 55 | } 56 | 57 | [[cpp11::register]] double sum_dbl_accumulate2_(SEXP x_sxp) { 58 | const cpp11::doubles x(x_sxp, false); 59 | return std::accumulate(x.cbegin(), x.cend(), 0.); 60 | } 61 | -------------------------------------------------------------------------------- /cpp11test/src/sum_int.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11/integers.hpp" 3 | 4 | [[cpp11::register]] double sum_int_for_(cpp11::integers x) { 5 | double sum = 0.; 6 | R_xlen_t n = x.size(); 7 | for (R_xlen_t i = 0; i < n; ++i) { 8 | sum += x[i]; 9 | } 10 | 11 | return sum; 12 | } 13 | 14 | [[cpp11::register]] double sum_int_for2_(SEXP x_) { 15 | cpp11::integers x(x_, false); 16 | 17 | double sum = 0.; 18 | R_xlen_t n = x.size(); 19 | for (R_xlen_t i = 0; i < n; ++i) { 20 | sum += x[i]; 21 | } 22 | return sum; 23 | } 24 | 25 | [[cpp11::register]] double sum_int_foreach_(cpp11::integers x) { 26 | double sum = 0.; 27 | for (auto v : x) { 28 | sum += v; 29 | } 30 | 31 | return sum; 32 | } 33 | 34 | [[cpp11::register]] double sum_int_accumulate_(cpp11::integers x) { 35 | return std::accumulate(x.cbegin(), x.cend(), 0.); 36 | } 37 | -------------------------------------------------------------------------------- /cpp11test/src/sum_rcpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // clang-format off 4 | #ifdef __clang__ 5 | # pragma clang diagnostic push 6 | # pragma clang diagnostic ignored "-Wattributes" 7 | #endif 8 | 9 | #ifdef __GNUC__ 10 | # pragma GCC diagnostic push 11 | # pragma GCC diagnostic ignored "-Wattributes" 12 | #endif 13 | // clang-format on 14 | 15 | [[cpp11::register]] SEXP rcpp_sum_dbl_for_(SEXP x_sxp) { 16 | Rcpp::NumericVector x(x_sxp); 17 | R_xlen_t n = x.size(); 18 | double sum = 0.; 19 | for (R_xlen_t i = 0; i < n; ++i) { 20 | sum += x[i]; 21 | } 22 | return Rf_ScalarReal(sum); 23 | } 24 | 25 | [[cpp11::register]] SEXP rcpp_sum_int_for_(SEXP x_sxp) { 26 | Rcpp::IntegerVector x(x_sxp); 27 | R_xlen_t n = x.size(); 28 | double sum = 0.; 29 | for (R_xlen_t i = 0; i < n; ++i) { 30 | sum += x[i]; 31 | } 32 | return Rf_ScalarReal(sum); 33 | } 34 | 35 | [[cpp11::register]] SEXP rcpp_sum_dbl_foreach_(SEXP x_sxp) { 36 | Rcpp::NumericVector x(x_sxp); 37 | double sum = 0.; 38 | for (const auto& val : x) { 39 | sum += val; 40 | } 41 | return Rf_ScalarReal(sum); 42 | } 43 | 44 | [[cpp11::register]] SEXP rcpp_sum_dbl_accumulate_(SEXP x_sxp) { 45 | Rcpp::NumericVector x(x_sxp); 46 | return Rf_ScalarReal(std::accumulate(x.cbegin(), x.cend(), 0.)); 47 | } 48 | 49 | [[cpp11::register]] SEXP rcpp_grow_(SEXP n_sxp) { 50 | R_xlen_t n = REAL(n_sxp)[0]; 51 | Rcpp::NumericVector x; 52 | R_xlen_t i = 0; 53 | while (i < n) { 54 | x.push_back(i++); 55 | } 56 | 57 | return x; 58 | } 59 | 60 | [[cpp11::register]] SEXP rcpp_push_and_truncate_(SEXP size_sxp) { 61 | R_xlen_t size = INTEGER(size_sxp)[0]; 62 | 63 | // Allocate `size` worth of doubles (filled with garbage data) 64 | Rcpp::NumericVector out(size); 65 | 66 | // Push 1 more past the existing capacity 67 | out.push_back(0); 68 | 69 | return out; 70 | } 71 | -------------------------------------------------------------------------------- /cpp11test/src/test-data_frame.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/data_frame.hpp" 2 | #include "cpp11/function.hpp" 3 | #include "cpp11/integers.hpp" 4 | #include "cpp11/strings.hpp" 5 | 6 | #include 7 | 8 | context("data_frame-C++") { 9 | test_that("data_frame works") { 10 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 11 | auto mtcars = getExportedValue("datasets", "mtcars"); 12 | cpp11::data_frame mtcars_df(mtcars); 13 | 14 | expect_true(mtcars_df.nrow() == 32); 15 | expect_true(mtcars_df.ncol() == 11); 16 | 17 | cpp11::strings names(mtcars_df.names()); 18 | expect_true(names[0] == "mpg"); 19 | expect_true(names[7] == "vs"); 20 | 21 | auto iris = getExportedValue("datasets", "iris"); 22 | cpp11::data_frame iris_df(iris); 23 | 24 | expect_true(iris_df.nrow() == 150); 25 | expect_true(iris_df.ncol() == 5); 26 | } 27 | 28 | test_that("data_frame::nrow works with 0x0 dfs") { 29 | SEXP x = PROTECT(Rf_allocVector(VECSXP, 0)); 30 | 31 | cpp11::data_frame df(x); 32 | expect_true(df.nrow() == 0); 33 | 34 | UNPROTECT(1); 35 | } 36 | 37 | test_that("data_frame::nrow works with 10x0 dfs") { 38 | using namespace cpp11::literals; 39 | cpp11::writable::list x(0_xl); 40 | x.attr(R_RowNamesSymbol) = {NA_INTEGER, -10}; 41 | 42 | cpp11::data_frame df(x); 43 | expect_true(df.nrow() == 10); 44 | } 45 | 46 | test_that("writable::data_frame::nrow works with 0x0 dfs") { 47 | SEXP x = PROTECT(Rf_allocVector(VECSXP, 0)); 48 | 49 | cpp11::writable::data_frame df(x); 50 | expect_true(df.nrow() == 0); 51 | 52 | UNPROTECT(1); 53 | } 54 | 55 | test_that("writable::data_frame::nrow works with 10x0 dfs (#272)") { 56 | SEXP x = PROTECT(Rf_allocVector(VECSXP, 0)); 57 | 58 | bool is_altrep = false; 59 | R_xlen_t nrow = 10; 60 | 61 | // Manually specify `nrow` using special constructor 62 | cpp11::writable::data_frame df(x, is_altrep, nrow); 63 | expect_true(df.nrow() == 10); 64 | 65 | UNPROTECT(1); 66 | } 67 | 68 | test_that("writable::data_frame works") { 69 | using namespace cpp11::literals; 70 | cpp11::writable::data_frame df({"x"_nm = {1, 2, 3}, "y"_nm = {"a", "b", "c"}}); 71 | auto nrows = df.nrow(); 72 | expect_true(df.nrow() == 3); 73 | expect_true(df.ncol() == 2); 74 | 75 | cpp11::strings names(df.names()); 76 | expect_true(names[0] == "x"); 77 | expect_true(names[1] == "y"); 78 | 79 | cpp11::integers x(df[0]); 80 | expect_true(x[0] == 1); 81 | expect_true(x[1] == 2); 82 | expect_true(x[2] == 3); 83 | 84 | cpp11::strings y(df[1]); 85 | expect_true(y[0] == "a"); 86 | expect_true(y[1] == "b"); 87 | expect_true(y[2] == "c"); 88 | 89 | SEXP out = df; 90 | 91 | std::string clazz( 92 | Rf_translateCharUTF8(STRING_ELT(Rf_getAttrib(out, R_ClassSymbol), 0))); 93 | expect_true(clazz == "data.frame"); 94 | 95 | cpp11::integers row_names(Rf_getAttrib(out, R_RowNamesSymbol)); 96 | expect_true(row_names[0] == 1); 97 | expect_true(row_names[1] == 2); 98 | expect_true(row_names[2] == 3); 99 | } 100 | 101 | test_that("can set attributes on a data_frame") { 102 | using namespace cpp11::literals; 103 | 104 | cpp11::writable::data_frame df({"x"_nm = {1, 2, 3}, "y"_nm = {"a", "b", "c"}}); 105 | 106 | df.attr("foo") = "bar"; 107 | 108 | cpp11::r_string foo = cpp11::strings(df.attr("foo"))[0]; 109 | expect_true(foo == "bar"); 110 | 111 | df.names() = {"a", "b"}; 112 | 113 | expect_true(cpp11::integers(df["a"])[0] == 1); 114 | expect_true(cpp11::strings(df["b"])[2] == "c"); 115 | } 116 | 117 | test_that("growing vectors uses proper length") { 118 | using namespace cpp11::literals; 119 | 120 | cpp11::writable::integers x, y; 121 | for (int i = 0; i < 10; ++i) { 122 | x.push_back(i); 123 | y.push_back(i); 124 | } 125 | cpp11::writable::data_frame out({"x"_nm = x, "y"_nm = y}); 126 | 127 | expect_true(out.nrow() == 10); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /cpp11test/src/test-environment.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/as.hpp" 2 | #include "cpp11/environment.hpp" 3 | #include "cpp11/function.hpp" 4 | #include "cpp11/strings.hpp" 5 | 6 | #include 7 | 8 | context("environment-C++") { 9 | test_that("environment works") { 10 | auto new_env = cpp11::package("base")["new.env"]; 11 | SEXP x_sxp = PROTECT(new_env()); 12 | 13 | cpp11::environment x(x_sxp); 14 | 15 | expect_true(x.size() == 0); 16 | 17 | x["foo"] = 1; 18 | expect_true(x.size() == 1); 19 | expect_true(cpp11::as_cpp(x["foo"]) == 1); 20 | 21 | x["bar"] = "hi"; 22 | expect_true(x.size() == 2); 23 | expect_true(cpp11::strings(x["bar"])[0] == "hi"); 24 | 25 | x.remove("bar"); 26 | expect_true(x.size() == 1); 27 | // Object must exist in the environment when we convert to SEXP 28 | auto bar_proxy = x["bar"]; 29 | expect_error_as(static_cast(bar_proxy), cpp11::unwind_exception); 30 | 31 | x.remove("foo"); 32 | expect_true(x.size() == 0); 33 | // Object must exist in the environment when we convert to SEXP 34 | auto foo_proxy = x["foo"]; 35 | expect_error_as(static_cast(foo_proxy), cpp11::unwind_exception); 36 | 37 | expect_true(static_cast(x) == x_sxp); 38 | 39 | UNPROTECT(1); 40 | } 41 | 42 | test_that("environment doesn't leak `R_MissingArg`") { 43 | auto new_env = cpp11::package("base")["new.env"]; 44 | SEXP x_sxp = PROTECT(new_env()); 45 | 46 | // Install `R_MissingArg` as a value in the environment 47 | Rf_defineVar(Rf_install("missing"), R_MissingArg, x_sxp); 48 | 49 | cpp11::environment x(x_sxp); 50 | expect_true(x.size() == 1); 51 | 52 | // Upon conversion to SEXP, we error on `R_MissingArg`. 53 | // Base R's `R_getVar()` tries hard not to leak this, so we do too. 54 | auto proxy = x["missing"]; 55 | expect_error_as(static_cast(proxy), cpp11::unwind_exception); 56 | 57 | UNPROTECT(1); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cpp11test/src/test-external_pointer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cpp11/external_pointer.hpp" 3 | 4 | #include 5 | 6 | bool deleted = false; 7 | 8 | void deleter(int* ptr) { 9 | deleted = true; 10 | delete ptr; 11 | } 12 | 13 | context("external_pointer-C++") { 14 | test_that("external_pointer works") { 15 | std::vector* v = new std::vector; 16 | v->push_back(1); 17 | v->push_back(2); 18 | 19 | cpp11::external_pointer> p(v); 20 | 21 | expect_true(p->at(0) == 1); 22 | expect_true(p->at(1) == 2); 23 | 24 | p.release(); 25 | 26 | // Double release should be a no-op 27 | p.release(); 28 | 29 | expect_true(p.get() == nullptr); 30 | } 31 | 32 | test_that("external_pointer works with a custom deleter") { 33 | deleted = false; 34 | cpp11::external_pointer uniq(new int); 35 | expect_true(deleted == false); 36 | uniq.reset(); 37 | expect_true(deleted == true); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cpp11test/src/test-function.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/function.hpp" 2 | 3 | #include 4 | 5 | context("function-C++") { 6 | test_that("functions can be called") { 7 | auto median = cpp11::package("stats")["median"]; 8 | double res = median(cpp11::as_sexp({1., 2., 3., NA_REAL}), true); 9 | expect_true(res == 2.); 10 | 11 | double res2 = median(cpp11::as_sexp({1., 2., 3., NA_REAL}), false); 12 | expect_true(ISNAN(res2)); 13 | } 14 | 15 | test_that("functions can be called with named arguments") { 16 | using namespace cpp11::literals; 17 | 18 | auto median = cpp11::package("stats")["median"]; 19 | double res = median("x"_nm = {1., 2., 3., NA_REAL}, "na.rm"_nm = true); 20 | expect_true(res == 2.); 21 | 22 | double res2 = median(cpp11::as_sexp({1., 2., 3., NA_REAL}), false); 23 | expect_true(ISNAN(res2)); 24 | } 25 | 26 | test_that("base functions can be called") { 27 | auto file = cpp11::package("base")["file"]; 28 | auto isOpen = cpp11::package("base")["isOpen"]; 29 | auto close = cpp11::package("base")["close"]; 30 | 31 | cpp11::sexp con = file("foo"); 32 | 33 | bool res = isOpen(con); 34 | 35 | expect_true(res == false); 36 | 37 | close(con); 38 | } 39 | 40 | test_that("unknown packages cause an error (#317)") { 41 | expect_error_as(cpp11::package("definitely_not_a_package"), cpp11::unwind_exception); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cpp11test/src/test-list.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/R.hpp" 2 | #include "cpp11/doubles.hpp" 3 | #include "cpp11/integers.hpp" 4 | #include "cpp11/list.hpp" 5 | #include "cpp11/logicals.hpp" 6 | #include "cpp11/protect.hpp" 7 | #include "cpp11/raws.hpp" 8 | #include "cpp11/strings.hpp" 9 | 10 | #include 11 | 12 | context("list-C++") { 13 | test_that("list.push_back()") { 14 | cpp11::writable::list x; 15 | 16 | x.push_back(cpp11::writable::doubles({1.})); 17 | x.push_back(cpp11::writable::integers({3, 4, 5})); 18 | x.push_back(cpp11::writable::strings({"foo", "bar"})); 19 | 20 | cpp11::writable::logicals lgl; 21 | lgl.push_back(TRUE); 22 | lgl.push_back(FALSE); 23 | lgl.push_back(TRUE); 24 | x.push_back(lgl); 25 | 26 | x.push_back(cpp11::writable::raws({'a', 'b', 'c'})); 27 | 28 | expect_true(x.size() == 5); 29 | 30 | cpp11::doubles first(x[0]); 31 | expect_true(first[0] == 1.); 32 | 33 | cpp11::integers second(x[1]); 34 | expect_true(second[0] == 3); 35 | expect_true(second[1] == 4); 36 | expect_true(second[2] == 5); 37 | 38 | cpp11::strings third(x[2]); 39 | expect_true(third[0] == "foo"); 40 | expect_true(third[1] == "bar"); 41 | 42 | cpp11::logicals fourth(x[3]); 43 | expect_true(fourth[0] == TRUE); 44 | expect_true(fourth[1] == FALSE); 45 | expect_true(fourth[2] == TRUE); 46 | 47 | cpp11::raws fifth(x[4]); 48 | expect_true(fifth[0] == 'a'); 49 | expect_true(fifth[1] == 'b'); 50 | expect_true(fifth[2] == 'c'); 51 | } 52 | 53 | test_that("unnamed_list.push_back(unnamed_arg)") { 54 | cpp11::writable::list x(1); 55 | x.push_back(cpp11::writable::integers(2)); 56 | 57 | expect_true(x.names() == R_NilValue); 58 | } 59 | 60 | test_that("unnamed_list.push_back(named_arg)") { 61 | using namespace cpp11::literals; 62 | 63 | cpp11::writable::list x(1); 64 | x.push_back("bar"_nm = 2); 65 | 66 | cpp11::strings nms(x.names()); 67 | 68 | expect_true(nms.size() == 2); 69 | expect_true(nms[0] == ""); 70 | expect_true(nms[1] == "bar"); 71 | } 72 | 73 | test_that("named_list.push_back(unnamed_arg)") { 74 | using namespace cpp11::literals; 75 | 76 | cpp11::writable::list x({"foo"_nm = 1}); 77 | x.push_back(cpp11::writable::integers(2)); 78 | 79 | cpp11::strings nms(x.names()); 80 | 81 | expect_true(nms.size() == 2); 82 | expect_true(nms[0] == "foo"); 83 | expect_true(nms[1] == ""); 84 | } 85 | 86 | test_that("named_list.push_back(named_arg)") { 87 | using namespace cpp11::literals; 88 | 89 | cpp11::writable::list x({"foo"_nm = 1}); 90 | x.push_back({"bar"_nm = 2}); 91 | 92 | cpp11::strings nms(x.names()); 93 | 94 | expect_true(nms.size() == 2); 95 | expect_true(nms[0] == "foo"); 96 | expect_true(nms[1] == "bar"); 97 | } 98 | 99 | test_that("empty_list.push_back(unnamed_arg)") { 100 | cpp11::writable::list x; 101 | x.push_back(cpp11::writable::integers(2)); 102 | 103 | expect_true(x.names() == R_NilValue); 104 | } 105 | 106 | test_that("empty_list.push_back(named_arg)") { 107 | using namespace cpp11::literals; 108 | 109 | cpp11::writable::list x; 110 | x.push_back({"bar"_nm = 2}); 111 | 112 | cpp11::strings nms(x.names()); 113 | 114 | expect_true(nms.size() == 1); 115 | expect_true(nms[0] == "bar"); 116 | } 117 | 118 | test_that("attribute setting works") { 119 | cpp11::writable::list x( 120 | {cpp11::writable::doubles({1, 2, 3}), cpp11::writable::strings({"x", "y", "z"})}); 121 | 122 | x.attr("names") = cpp11::writable::strings({"x", "y"}); 123 | x.attr("class") = "data.frame"; 124 | x.attr("rownames") = cpp11::writable::doubles({-3, NA_REAL}); 125 | 126 | expect_true(Rf_inherits(x, "data.frame")); 127 | } 128 | 129 | test_that("list::iterator uses VECTOR_ELT") { 130 | cpp11::writable::list x({cpp11::writable::integers({1, 2})}); 131 | cpp11::integers first(*x.begin()); 132 | expect_true(first[0] == 1); 133 | expect_true(first[1] == 2); 134 | } 135 | 136 | test_that("list.named() works") { 137 | using namespace cpp11::literals; 138 | 139 | cpp11::writable::list x({"bar"_nm = 2}); 140 | expect_true(x.named()); 141 | 142 | cpp11::writable::list y(1); 143 | expect_false(y.named()); 144 | } 145 | 146 | test_that("list.empty() works") { 147 | cpp11::writable::list x; 148 | 149 | expect_true(x.empty()); 150 | 151 | cpp11::writable::list y(1); 152 | 153 | expect_false(y.empty()); 154 | } 155 | 156 | test_that("names of named lists are also resized") { 157 | using namespace cpp11::literals; 158 | 159 | cpp11::writable::list x; 160 | x.push_back({"n1"_nm = 1}); 161 | x.push_back({"n2"_nm = 2}); 162 | x.push_back({"n3"_nm = 3}); 163 | x.push_back({"n4"_nm = 4}); 164 | x.push_back({"n5"_nm = 5}); 165 | x = SEXP(x); 166 | 167 | cpp11::strings nms(x.names()); 168 | expect_true(x.size() == nms.size()); 169 | } 170 | 171 | test_that("list::operator[] and at by name") { 172 | SEXP x = PROTECT(Rf_allocVector(VECSXP, 1)); 173 | 174 | SEXP elt = Rf_allocVector(INTSXP, 1); 175 | SET_VECTOR_ELT(x, 0, elt); 176 | SET_INTEGER_ELT(elt, 0, 1); 177 | 178 | SEXP names = Rf_allocVector(STRSXP, 1); 179 | Rf_setAttrib(x, R_NamesSymbol, names); 180 | SET_STRING_ELT(names, 0, Rf_mkCharCE("name", CE_UTF8)); 181 | 182 | cpp11::list lst(x); 183 | 184 | expect_true(lst.named()); 185 | expect_true(lst["name"] == elt); 186 | expect_true(lst.at("name") == elt); 187 | 188 | // Lists are the only class where OOB accesses by name return `NULL` 189 | expect_true(lst["oob"] == R_NilValue); 190 | expect_true(lst.at("oob") == R_NilValue); 191 | 192 | UNPROTECT(1); 193 | } 194 | 195 | test_that("We don't return NULL for default constructed vectors") { 196 | cpp11::writable::list x; 197 | SEXP y(x); 198 | 199 | expect_true(Rf_xlength(y) == 0); 200 | expect_true(y != R_NilValue); 201 | } 202 | 203 | test_that("writable::list(initializer_list)") { 204 | using namespace cpp11::literals; 205 | 206 | SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1)); 207 | SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2)); 208 | SEXP x3 = PROTECT(Rf_allocVector(STRSXP, 3)); 209 | 210 | // Note that `x1`, `x2`, and `x3` are list elements, not lists of length 1! 211 | cpp11::writable::list x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3}); 212 | expect_true(x.named()); 213 | expect_true(x["one"] == x1); 214 | expect_true(x["two"] == x2); 215 | expect_true(x["three"] == x3); 216 | 217 | // This also works, with varying types 218 | cpp11::writable::list y({"one"_nm = 1, "two"_nm = true, "three"_nm = 2.5}); 219 | expect_true(y.named()); 220 | expect_true(cpp11::detail::r_typeof(y["one"]) == INTSXP); 221 | expect_true(cpp11::detail::r_typeof(y["two"]) == LGLSXP); 222 | expect_true(cpp11::detail::r_typeof(y["three"]) == REALSXP); 223 | 224 | UNPROTECT(3); 225 | } 226 | 227 | test_that("writable::list(initializer_list)") { 228 | SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1)); 229 | SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2)); 230 | SEXP x3 = PROTECT(Rf_allocVector(STRSXP, 3)); 231 | 232 | cpp11::writable::list x({x1, x2, x3}); 233 | expect_true(x[0] == x1); 234 | expect_true(x[1] == x2); 235 | expect_true(x[2] == x3); 236 | 237 | UNPROTECT(3); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /cpp11test/src/test-list_of.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | #include "cpp11/list.hpp" 3 | #include "cpp11/list_of.hpp" 4 | #include "cpp11/strings.hpp" 5 | 6 | #include 7 | 8 | context("list_of-C++") { 9 | test_that("list_of works") { 10 | using namespace cpp11::literals; 11 | 12 | cpp11::writable::list x({"x"_nm = cpp11::writable::doubles({1., 2., 3.}), 13 | "y"_nm = cpp11::writable::doubles({4., 5., 6.})}); 14 | 15 | cpp11::list_of res(x); 16 | 17 | expect_true(res.size() == 2); 18 | expect_true(res[0][0] == 1.); 19 | expect_true(res[1][0] == 4.); 20 | expect_true(res.names()[0] == "x"); 21 | expect_true(res.names()[1] == "y"); 22 | } 23 | 24 | test_that("writable::list_of works") { 25 | using namespace cpp11::literals; 26 | 27 | cpp11::writable::list x({"x"_nm = cpp11::writable::doubles({1., 2., 3.}), 28 | "y"_nm = cpp11::writable::doubles({4., 5., 6.})}); 29 | 30 | cpp11::writable::list_of res(x); 31 | 32 | res.push_back({"z"_nm = cpp11::writable::doubles({7., 8., 9.})}); 33 | 34 | expect_true(res.size() == 3); 35 | expect_true(REAL(VECTOR_ELT(res, 2))[0] == 7.); 36 | 37 | res[0][0] = 2.; 38 | 39 | expect_true(REAL(VECTOR_ELT(res, 0))[0] == 2.); 40 | 41 | res[1] = cpp11::writable::doubles({7., 8., 9.}); 42 | 43 | expect_true(REAL(res[1])[0] == 7.); 44 | expect_true(REAL(res[1])[1] == 8.); 45 | expect_true(REAL(res[1])[2] == 9.); 46 | 47 | res["x"][0] = 4; 48 | expect_true(REAL(res[0])[0] == 4.); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cpp11test/src/test-logicals.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/logicals.hpp" 2 | 3 | #include 4 | 5 | context("logicals-C++") { 6 | test_that("logicals.push_back()") { 7 | cpp11::writable::logicals x; 8 | x.push_back(TRUE); 9 | x.push_back(FALSE); 10 | 11 | expect_true(x.size() == 2); 12 | expect_true(x[0] == TRUE); 13 | expect_true(x[1] == FALSE); 14 | } 15 | test_that("logicals.resize()") { 16 | cpp11::writable::logicals x; 17 | x.resize(2); 18 | x[0] = TRUE; 19 | x[1] = FALSE; 20 | 21 | expect_true(x.size() == 2); 22 | expect_true(x[0] == TRUE); 23 | expect_true(x[1] == FALSE); 24 | } 25 | test_that("logicals.at()") { 26 | cpp11::writable::logicals x; 27 | 28 | expect_error(x.at(-1)); 29 | 30 | expect_error(x.at(0)); 31 | 32 | x.push_back(TRUE); 33 | expect_true(x.at(0) == TRUE); 34 | expect_error(x.at(1)); 35 | } 36 | test_that("logicals.pop_back()") { 37 | cpp11::writable::logicals x; 38 | 39 | x.push_back(TRUE); 40 | x.push_back(FALSE); 41 | x.pop_back(); 42 | 43 | expect_true(x.size() == 1); 44 | expect_true(x[0] == TRUE); 45 | 46 | expect_error(x.at(1)); 47 | } 48 | test_that("logicals.insert()") { 49 | cpp11::writable::logicals x; 50 | 51 | x.insert(0, TRUE); 52 | x.insert(0, FALSE); 53 | x.insert(1, TRUE); 54 | expect_true(x.size() == 3); 55 | 56 | expect_true(x[0] == FALSE); 57 | expect_true(x[1] == TRUE); 58 | expect_true(x[2] == TRUE); 59 | } 60 | test_that("logicals.erase()") { 61 | cpp11::writable::logicals x; 62 | 63 | x.push_back(TRUE); 64 | x.push_back(FALSE); 65 | x.push_back(NA_LOGICAL); 66 | x.push_back(FALSE); 67 | x.push_back(TRUE); 68 | 69 | expect_true(x.size() == 5); 70 | 71 | x.erase(0); 72 | 73 | expect_true(x.size() == 4); 74 | expect_true(x[0] == FALSE); 75 | expect_true(x[1] == NA_LOGICAL); 76 | expect_true(x[2] == FALSE); 77 | expect_true(x[3] == TRUE); 78 | 79 | x.erase(2); 80 | 81 | expect_true(x.size() == 3); 82 | expect_true(x[0] == FALSE); 83 | expect_true(x[1] == NA_LOGICAL); 84 | expect_true(x[2] == TRUE); 85 | } 86 | test_that("logicals.iterator* = ") { 87 | cpp11::writable::logicals x; 88 | x.push_back(TRUE); 89 | x.push_back(FALSE); 90 | x.push_back(FALSE); 91 | auto it = x.begin() + 1; 92 | *it = TRUE; 93 | ++it; 94 | *it = FALSE; 95 | 96 | expect_true(x.size() == 3); 97 | expect_true(x[0] == TRUE); 98 | expect_true(x[1] == TRUE); 99 | expect_true(x[2] == FALSE); 100 | } 101 | 102 | test_that("writable::logicals(SEXP)") { 103 | SEXP x = PROTECT(Rf_allocVector(LGLSXP, 5)); 104 | 105 | LOGICAL(x)[0] = TRUE; 106 | LOGICAL(x)[1] = TRUE; 107 | LOGICAL(x)[2] = TRUE; 108 | LOGICAL(x)[3] = TRUE; 109 | LOGICAL(x)[4] = TRUE; 110 | 111 | cpp11::writable::logicals y(x); 112 | y[0] = FALSE; 113 | 114 | expect_true(x != y.data()); 115 | 116 | expect_true(LOGICAL(x)[0] == 1); 117 | expect_true(y[0] == FALSE); 118 | 119 | cpp11::writable::logicals z(y); 120 | z[0] = TRUE; 121 | 122 | expect_true(z.data() != y.data()); 123 | 124 | expect_true(LOGICAL(x)[0] == 1); 125 | expect_true(y[0] == FALSE); 126 | expect_true(z[0] == TRUE); 127 | 128 | UNPROTECT(1); 129 | } 130 | 131 | test_that("writable::logicals(initializer_list)") { 132 | using namespace cpp11::literals; 133 | 134 | SEXP x1 = PROTECT(Rf_allocVector(LGLSXP, 1)); 135 | SEXP x2 = PROTECT(Rf_allocVector(LGLSXP, 1)); 136 | SEXP x3 = PROTECT(Rf_allocVector(LGLSXP, 1)); 137 | 138 | SET_LOGICAL_ELT(x1, 0, 0); 139 | SET_LOGICAL_ELT(x2, 0, 1); 140 | SET_LOGICAL_ELT(x3, 0, NA_LOGICAL); 141 | 142 | // From scalar logical vectors 143 | cpp11::writable::logicals x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3}); 144 | expect_true(x.named()); 145 | expect_true(x["one"] == cpp11::r_bool(false)); 146 | expect_true(x["two"] == cpp11::r_bool(true)); 147 | expect_true(x["three"] == cpp11::r_bool(NA_LOGICAL)); 148 | 149 | // From booleans 150 | cpp11::writable::logicals y({"one"_nm = true, "two"_nm = false, "three"_nm = true}); 151 | expect_true(y.named()); 152 | expect_true(y["one"] == cpp11::r_bool(true)); 153 | expect_true(y["two"] == cpp11::r_bool(false)); 154 | expect_true(y["three"] == cpp11::r_bool(true)); 155 | 156 | UNPROTECT(3); 157 | } 158 | 159 | test_that("writable::logicals(initializer_list) type check") { 160 | using namespace cpp11::literals; 161 | expect_error_as(cpp11::writable::logicals({"one"_nm = 1}), cpp11::type_error); 162 | expect_error_as(cpp11::writable::logicals({"one"_nm = R_NilValue}), 163 | cpp11::type_error); 164 | } 165 | 166 | test_that("writable::logicals(initializer_list) length check") { 167 | using namespace cpp11::literals; 168 | SEXP x = PROTECT(Rf_allocVector(LGLSXP, 2)); 169 | expect_error_as(cpp11::writable::logicals({"x"_nm = x}), std::length_error); 170 | UNPROTECT(1); 171 | } 172 | 173 | test_that("writable::logicals(initializer_list)") { 174 | cpp11::writable::logicals x( 175 | {cpp11::r_bool(true), cpp11::r_bool(false), cpp11::r_bool(NA_INTEGER)}); 176 | expect_true(x[0] == cpp11::r_bool(true)); 177 | expect_true(x[1] == cpp11::r_bool(false)); 178 | expect_true(x[2] == cpp11::r_bool(NA_INTEGER)); 179 | 180 | // This works due to implicit conversion of `bool` to `r_bool` 181 | cpp11::writable::logicals y({true, false, false}); 182 | expect_true(y[0] == cpp11::r_bool(true)); 183 | expect_true(y[1] == cpp11::r_bool(false)); 184 | expect_true(y[2] == cpp11::r_bool(false)); 185 | 186 | // This works due to implicit conversion of `Rboolean` to `r_bool` 187 | cpp11::writable::logicals z({TRUE, FALSE, FALSE}); 188 | expect_true(z[0] == cpp11::r_bool(true)); 189 | expect_true(z[1] == cpp11::r_bool(false)); 190 | expect_true(z[2] == cpp11::r_bool(false)); 191 | } 192 | 193 | test_that("is_na(r_bool)") { 194 | cpp11::r_bool x = TRUE; 195 | expect_true(!cpp11::is_na(x)); 196 | 197 | cpp11::r_bool y = NA_LOGICAL; 198 | expect_true(cpp11::is_na(y)); 199 | } 200 | 201 | test_that("FALSE and false") { 202 | cpp11::writable::logicals x{FALSE}; 203 | expect_true(x.size() == 1); 204 | expect_true(x[0] == FALSE); 205 | 206 | cpp11::writable::logicals y{false}; 207 | expect_true(y.size() == 1); 208 | expect_true(y[0] == FALSE); 209 | } 210 | 211 | // test_that("writable::logicals(ALTREP_SEXP)") { 212 | // SEXP x = PROTECT(R_compact_intrange(1, 5)); 213 | //// Need to find (or create) an altrep class that implements duplicate. 214 | 215 | // cpp11::writable::logicals y(x); 216 | // y[0] = -1; 217 | 218 | // expect_true(x != y.data()); 219 | 220 | // expect_true(logical_ELT(x, 0) == 1); 221 | // expect_true(y[0] == -1); 222 | 223 | // cpp11::writable::logicals z(y); 224 | // z[0] = -2; 225 | 226 | // expect_true(z.data() != y.data()); 227 | 228 | // expect_true(logical_ELT(x, 0) == 1); 229 | // expect_true(y[0] == -1); 230 | // expect_true(z[0] == -2); 231 | 232 | // z.push_back(6); 233 | // expect_true(z[5] == 6); 234 | 235 | // UNPROTECT(1); 236 | //} 237 | } 238 | -------------------------------------------------------------------------------- /cpp11test/src/test-matrix.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | #include "cpp11/function.hpp" 3 | #include "cpp11/integers.hpp" 4 | #include "cpp11/matrix.hpp" 5 | 6 | #include 7 | 8 | context("matrix-C++") { 9 | test_that("matrix dim attributes are correct for writable matrices") { 10 | cpp11::writable::doubles_matrix x(5, 2); 11 | 12 | cpp11::integers dim(SEXP(x.attr("dim"))); 13 | 14 | expect_true(dim[0] == 5); 15 | expect_true(dim[1] == 2); 16 | 17 | expect_true(x.nrow() == 5); 18 | expect_true(x.ncol() == 2); 19 | expect_true(x.nslices() == 5); 20 | expect_true(x.slice_size() == 2); 21 | expect_true(x.slice_stride() == 5); 22 | expect_true(x.slice_offset(0) == 0); 23 | expect_true(x.slice_offset(1) == 1); 24 | expect_true(x[1].size() == 2); 25 | expect_true(x[1].stride() == 5); 26 | } 27 | test_that("matrix dim attributes are correct for read only matrices") { 28 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 29 | 30 | test_that("matrix attributes are correct") { 31 | cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); 32 | 33 | expect_true(x.size() == 5307); 34 | expect_true(x.nrow() == 87); 35 | expect_true(x.ncol() == 61); 36 | expect_true(x.nslices() == 87); 37 | expect_true(x.slice_size() == 61); 38 | expect_true(x.slice_stride() == 87); 39 | expect_true(x.slice_offset(0) == 0); 40 | expect_true(x.slice_offset(1) == 1); 41 | expect_true(x[1].size() == 61); 42 | expect_true(x[1].stride() == 87); 43 | } 44 | test_that("matrix attributes are correct") { 45 | cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); 46 | 47 | expect_true(x.size() == 5307); 48 | expect_true(x.nrow() == 87); 49 | expect_true(x.ncol() == 61); 50 | expect_true(x.nslices() == 61); 51 | expect_true(x.slice_size() == 87); 52 | expect_true(x.slice_stride() == 1); 53 | expect_true(x.slice_offset(0) == 0); 54 | expect_true(x.slice_offset(1) == 87); 55 | expect_true(x[1].size() == 87); 56 | expect_true(x[1].stride() == 1); 57 | } 58 | } 59 | 60 | test_that("row based subsetting works") { 61 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 62 | 63 | cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); 64 | expect_true(x.nslices() == 87); 65 | expect_true(x.slice_size() == 61); 66 | 67 | auto r = x[0]; 68 | expect_true(r[0] == 100); 69 | expect_true(r[60] == 103); 70 | 71 | auto r2 = x[2]; 72 | expect_true(r2[0] == 102); 73 | expect_true(r2[60] == 104); 74 | } 75 | 76 | test_that("column based subsetting works") { 77 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 78 | 79 | cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); 80 | expect_true(x.nslices() == 61); 81 | expect_true(x.slice_size() == 87); 82 | 83 | auto c = x[0]; 84 | expect_true(c[0] == 100); 85 | expect_true(c[86] == 97); 86 | 87 | auto c2 = x[5]; 88 | expect_true(c2[0] == 101); 89 | expect_true(c2[86] == 99); 90 | } 91 | 92 | test_that("index based subsetting works") { 93 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 94 | 95 | cpp11::doubles_matrix xr(getExportedValue("datasets", "volcano")); 96 | expect_true(xr(0, 0) == 100); 97 | expect_true(xr(0, 60) == 103); 98 | expect_true(xr(10, 13) == 121); 99 | 100 | cpp11::doubles_matrix xc(getExportedValue("datasets", "volcano")); 101 | expect_true(xc(0, 0) == 100); 102 | expect_true(xc(0, 60) == 103); 103 | expect_true(xc(10, 13) == 121); 104 | } 105 | 106 | test_that("copy constructor works for read only matrices") { 107 | auto getExportedValue = cpp11::package("base")["getExportedValue"]; 108 | cpp11::doubles_matrix x(getExportedValue("datasets", "volcano")); 109 | 110 | cpp11::doubles_matrix yr(x); 111 | expect_true(x.nrow() == yr.nrow()); 112 | expect_true(x.ncol() == yr.ncol()); 113 | expect_true(yr.nslices() == yr.nrow()); 114 | expect_true(SEXP(x) == SEXP(yr)); 115 | 116 | cpp11::doubles_matrix yc(x); 117 | expect_true(x.nrow() == yc.nrow()); 118 | expect_true(x.ncol() == yc.ncol()); 119 | expect_true(yc.nslices() == yc.ncol()); 120 | expect_true(SEXP(x) == SEXP(yc)); 121 | } 122 | 123 | test_that("copy constructor works for writable matrices") { 124 | cpp11::writable::doubles_matrix x(5, 2); 125 | 126 | auto x_dim = x.attr("dim"); 127 | expect_true(INTEGER_ELT(x_dim, 0) == 5); 128 | expect_true(INTEGER_ELT(x_dim, 1) == 2); 129 | 130 | cpp11::writable::doubles_matrix yr(x); 131 | expect_true(x.nrow() == yr.nrow()); 132 | expect_true(x.ncol() == yr.ncol()); 133 | expect_true(yr.nslices() == yr.nrow()); 134 | // Note that a copy should be made when copying writable! 135 | expect_true(SEXP(x) != SEXP(yr)); 136 | 137 | // `dim` attribute is retained on copy 138 | auto yr_dim = yr.attr("dim"); 139 | expect_true(INTEGER_ELT(yr_dim, 0) == 5); 140 | expect_true(INTEGER_ELT(yr_dim, 1) == 2); 141 | 142 | cpp11::writable::doubles_matrix yc(x); 143 | expect_true(x.nrow() == yc.nrow()); 144 | expect_true(x.ncol() == yc.ncol()); 145 | expect_true(yc.nslices() == yc.ncol()); 146 | // Note that a copy should be made when copying writable! 147 | expect_true(SEXP(x) != SEXP(yc)); 148 | 149 | // `dim` attribute is retained on copy 150 | auto yc_dim = yc.attr("dim"); 151 | expect_true(INTEGER_ELT(yc_dim, 0) == 5); 152 | expect_true(INTEGER_ELT(yc_dim, 1) == 2); 153 | } 154 | 155 | test_that("copy constructor is not enabled across vector types") { 156 | cpp11::writable::doubles_matrix x(5, 2); 157 | expect_error(cpp11::writable::integers_matrix(x)); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /cpp11test/src/test-nas.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | #include "cpp11/integers.hpp" 3 | #include "cpp11/r_bool.hpp" 4 | #include "cpp11/r_string.hpp" 5 | 6 | #include 7 | 8 | context("nas-C++") { 9 | test_that("na integer") { expect_true(cpp11::na() == NA_INTEGER); } 10 | test_that("na double") { expect_true(ISNA(cpp11::na())); } 11 | test_that("na bool") { expect_true(cpp11::na() == NA_LOGICAL); } 12 | test_that("na string") { expect_true(cpp11::na() == NA_STRING); } 13 | } 14 | -------------------------------------------------------------------------------- /cpp11test/src/test-protect-nested.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/function.hpp" 2 | #include "cpp11/protect.hpp" 3 | #include "testthat.h" 4 | 5 | /* 6 | * See https://github.com/r-lib/cpp11/pull/327 for full details. 7 | * 8 | * - `cpp11::package("cpp11test")["test_destruction_outer"]` uses 9 | * `unwind_protect()` to call R level `test_destruction_outer()` but no entry 10 | * macros are set up. Instead we are going to catch exceptions that get here 11 | * with `expect_error_as()`. 12 | * 13 | * - Call R level `test_destruction_outer()` to set up `BEGIN_CPP11` / 14 | * `END_CPP11` entry macros. 15 | * 16 | * - C++ `test_destruction_outer()` goes through `unwind_protect()` to call 17 | * the R level `test_destruction_inner()`. 18 | * 19 | * - R level `test_destruction_inner()` sets up its own `BEGIN_CPP11` / 20 | * `END_CPP11` entry macros. 21 | * 22 | * - C++ `test_destruction_inner()` goes through `unwind_protect()` to call 23 | * `Rf_error()` (i.e., we are nested within `unwind_protect()`s!). 24 | * 25 | * - `longjmp()` is caught from inner `unwind_protect()`, and an exception 26 | * is thrown which is caught by the inner entry macros, allowing us to run 27 | * the destructor of `x`, then we let R continue the unwind process. 28 | * 29 | * - This `longjmp()`s again and is caught by the outer `unwind_protect()`, an 30 | * exception is thrown which is caught by the outer entry macros, and we let 31 | * R continue the unwind process one more time. 32 | * 33 | * - This `longjmp()` is caught by `cpp11::package()`'s `unwind_protect()`, 34 | * an exception is thrown, and that is caught by `expect_error_as()`. 35 | */ 36 | 37 | // Global variable to detect if the destructor has been run or not 38 | static bool destructed = false; 39 | 40 | class HasDestructor { 41 | public: 42 | ~HasDestructor(); 43 | }; 44 | 45 | HasDestructor::~HasDestructor() { 46 | // Destructor has run! 47 | destructed = true; 48 | } 49 | 50 | [[cpp11::register]] void test_destruction_inner() { 51 | // Expect that `x`'s destructor gets to run on the way out 52 | HasDestructor x{}; 53 | cpp11::stop("oh no!"); 54 | } 55 | 56 | [[cpp11::register]] void test_destruction_outer() { 57 | const auto test_destruction_inner = 58 | cpp11::package("cpp11test")["test_destruction_inner"]; 59 | test_destruction_inner(); 60 | } 61 | 62 | context("unwind_protect-nested-C++") { 63 | test_that( 64 | "nested `unwind_protect()` (with entry macros set up) will run destructors" 65 | "(#327)") { 66 | const auto fn = [&] { 67 | const auto test_destruction_outer = 68 | cpp11::package("cpp11test")["test_destruction_outer"]; 69 | test_destruction_outer(); 70 | }; 71 | 72 | expect_error_as(fn(), cpp11::unwind_exception); 73 | expect_true(destructed); 74 | 75 | destructed = false; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /cpp11test/src/test-protect.cpp: -------------------------------------------------------------------------------- 1 | #define CPP11_USE_FMT 2 | #include "cpp11/protect.hpp" 3 | #include "testthat.h" 4 | 5 | context("unwind_protect-C++") { 6 | test_that("unwind_protect works if there is no error") { 7 | SEXP out = PROTECT(cpp11::unwind_protect([&] { 8 | out = PROTECT(Rf_allocVector(REALSXP, 1)); 9 | REAL(out)[0] = 1; 10 | UNPROTECT(1); 11 | return out; 12 | })); 13 | 14 | expect_true(Rf_xlength(out) == 1); 15 | expect_true(REAL(out)[0] == 1); 16 | 17 | UNPROTECT(1); 18 | } 19 | test_that("unwind_protect throws a C++ exception if there is an R error") { 20 | SEXP out; 21 | expect_error_as(cpp11::unwind_protect([&] { 22 | out = PROTECT(Rf_allocVector(REALSXP, -1)); 23 | REAL(out)[0] = 1; 24 | UNPROTECT(1); 25 | return out; 26 | }), 27 | cpp11::unwind_exception); 28 | } 29 | 30 | test_that("safe wraps R functions and works if there is no error") { 31 | SEXP out = PROTECT(cpp11::safe[Rf_allocVector](REALSXP, 1)); 32 | REAL(out)[0] = 1; 33 | 34 | expect_true(Rf_xlength(out) == 1); 35 | expect_true(REAL(out)[0] == 1); 36 | 37 | UNPROTECT(1); 38 | } 39 | 40 | test_that("stop throws an unwind_exception") { 41 | expect_error_as(cpp11::stop("error"), cpp11::unwind_exception); 42 | expect_error_as(cpp11::stop("error {}", "message"), cpp11::unwind_exception); 43 | expect_error_as(cpp11::stop("error {a}", fmt::arg("a", "message")), 44 | cpp11::unwind_exception); 45 | } 46 | 47 | test_that("safe wraps R functions and works if there is an R error") { 48 | expect_error_as(cpp11::safe[Rf_allocVector](REALSXP, -1), cpp11::unwind_exception); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cpp11test/src/test-raws.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/raws.hpp" 2 | 3 | #include "Rversion.h" 4 | 5 | #include 6 | 7 | context("raws-C++") { 8 | test_that("raws.push_back()") { 9 | cpp11::writable::raws x; 10 | x.push_back('a'); 11 | x.push_back('b'); 12 | 13 | expect_true(x.size() == 2); 14 | expect_true(x[0] == 'a'); 15 | expect_true(x[1] == 'b'); 16 | } 17 | test_that("raws.resize()") { 18 | cpp11::writable::raws x; 19 | x.resize(2); 20 | x[0] = 'a'; 21 | x[1] = 'b'; 22 | 23 | expect_true(x.size() == 2); 24 | expect_true(x[0] == 'a'); 25 | expect_true(x[1] == 'b'); 26 | } 27 | test_that("raws.at()") { 28 | cpp11::writable::raws x; 29 | 30 | expect_error(x.at(-1)); 31 | 32 | expect_error(x.at(0)); 33 | 34 | x.push_back('a'); 35 | expect_true(x.at(0) == 'a'); 36 | expect_error(x.at(1)); 37 | } 38 | test_that("raws.pop_back()") { 39 | cpp11::writable::raws x; 40 | 41 | x.push_back('a'); 42 | x.push_back('b'); 43 | x.pop_back(); 44 | 45 | expect_true(x.size() == 1); 46 | expect_true(x[0] == 'a'); 47 | 48 | expect_error(x.at(1)); 49 | } 50 | test_that("raws.insert()") { 51 | cpp11::writable::raws x; 52 | 53 | x.insert(0, 'a'); 54 | x.insert(0, 'b'); 55 | x.insert(1, 'c'); 56 | expect_true(x.size() == 3); 57 | 58 | expect_true(x[0] == 'b'); 59 | expect_true(x[1] == 'c'); 60 | expect_true(x[2] == 'a'); 61 | } 62 | test_that("raws.erase()") { 63 | cpp11::writable::raws x; 64 | 65 | x.push_back('a'); 66 | x.push_back('b'); 67 | x.push_back('c'); 68 | x.push_back('d'); 69 | x.push_back('e'); 70 | 71 | expect_true(x.size() == 5); 72 | 73 | x.erase(0); 74 | 75 | expect_true(x.size() == 4); 76 | expect_true(x[0] == 'b'); 77 | expect_true(x[1] == 'c'); 78 | expect_true(x[2] == 'd'); 79 | expect_true(x[3] == 'e'); 80 | 81 | x.erase(2); 82 | 83 | expect_true(x.size() == 3); 84 | expect_true(x[0] == 'b'); 85 | expect_true(x[1] == 'c'); 86 | expect_true(x[2] == 'e'); 87 | } 88 | test_that("raws.iterator* = ") { 89 | cpp11::writable::raws x; 90 | x.push_back('a'); 91 | x.push_back('b'); 92 | x.push_back('c'); 93 | auto it = x.begin() + 1; 94 | *it = 'd'; 95 | ++it; 96 | *it = 'e'; 97 | 98 | expect_true(x.size() == 3); 99 | expect_true(x[0] == 'a'); 100 | expect_true(x[1] == 'd'); 101 | expect_true(x[2] == 'e'); 102 | } 103 | 104 | test_that("writable::raws(SEXP)") { 105 | SEXP x = PROTECT(Rf_allocVector(RAWSXP, 5)); 106 | 107 | RAW(x)[0] = 'a'; 108 | RAW(x)[1] = 'b'; 109 | RAW(x)[2] = 'c'; 110 | RAW(x)[3] = 'd'; 111 | RAW(x)[4] = 'e'; 112 | 113 | cpp11::writable::raws y(x); 114 | y[0] = '\0'; 115 | 116 | expect_true(x != y.data()); 117 | 118 | expect_true(RAW(x)[0] == 'a'); 119 | expect_true(y[0] == '\0'); 120 | 121 | cpp11::writable::raws z(y); 122 | z[0] = 'f'; 123 | 124 | expect_true(z.data() != y.data()); 125 | 126 | expect_true(RAW(x)[0] == 'a'); 127 | expect_true(y[0] == '\0'); 128 | expect_true(z[0] == 'f'); 129 | 130 | UNPROTECT(1); 131 | } 132 | 133 | test_that("writable::raws(initializer_list)") { 134 | using namespace cpp11::literals; 135 | 136 | SEXP x1 = PROTECT(Rf_allocVector(RAWSXP, 1)); 137 | SEXP x2 = PROTECT(Rf_allocVector(RAWSXP, 1)); 138 | SEXP x3 = PROTECT(Rf_allocVector(RAWSXP, 1)); 139 | 140 | #if R_VERSION >= R_Version(4, 2, 0) 141 | SET_RAW_ELT(x1, 0, 1); 142 | SET_RAW_ELT(x2, 0, 2); 143 | SET_RAW_ELT(x3, 0, 255); 144 | #else 145 | RAW(x1)[0] = 1; 146 | RAW(x2)[0] = 2; 147 | RAW(x3)[0] = 255; 148 | #endif 149 | 150 | // From scalar raw vectors 151 | cpp11::writable::raws x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3}); 152 | expect_true(x.named()); 153 | expect_true(x["one"] == 1); 154 | expect_true(x["two"] == 2); 155 | expect_true(x["three"] == 255); 156 | 157 | UNPROTECT(3); 158 | } 159 | 160 | test_that("writable::raws(initializer_list) type check") { 161 | using namespace cpp11::literals; 162 | expect_error_as(cpp11::writable::raws({"one"_nm = true}), cpp11::type_error); 163 | expect_error_as(cpp11::writable::raws({"one"_nm = R_NilValue}), cpp11::type_error); 164 | 165 | // `as_sexp()` turns this into an `INTSXP`, which is not compatible 166 | expect_error_as(cpp11::writable::raws({"one"_nm = 1}), cpp11::type_error); 167 | } 168 | 169 | test_that("writable::raws(initializer_list) length check") { 170 | using namespace cpp11::literals; 171 | SEXP x = PROTECT(Rf_allocVector(RAWSXP, 2)); 172 | expect_error_as(cpp11::writable::raws({"x"_nm = x}), std::length_error); 173 | UNPROTECT(1); 174 | } 175 | 176 | test_that("writable::raws(initializer_list)") { 177 | cpp11::writable::raws x({1, 2, 255}); 178 | expect_true(x[0] == 1); 179 | expect_true(x[1] == 2); 180 | expect_true(x[2] == 255); 181 | } 182 | 183 | // test_that("writable::raws(ALTREP_SEXP)") { 184 | // SEXP x = PROTECT(R_compact_uint8_trange(1, 5)); 185 | //// Need to find (or create) an altrep class that implements duplicate. 186 | 187 | // cpp11::writable::raws y(x); 188 | // y[0] = -1; 189 | 190 | // expect_true(x != y.data()); 191 | 192 | // expect_true(raw_ELT(x, 0) == 1); 193 | // expect_true(y[0] == -1); 194 | 195 | // cpp11::writable::raws z(y); 196 | // z[0] = -2; 197 | 198 | // expect_true(z.data() != y.data()); 199 | 200 | // expect_true(raw_ELT(x, 0) == 1); 201 | // expect_true(y[0] == -1); 202 | // expect_true(z[0] == -2); 203 | 204 | // z.push_back(6); 205 | // expect_true(z[5] == 6); 206 | 207 | // UNPROTECT(1); 208 | //} 209 | } 210 | -------------------------------------------------------------------------------- /cpp11test/src/test-runner.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file -- it ensures that your package will export a 3 | * 'run_testthat_tests()' C routine that can be used to run the Catch unit tests 4 | * available in your package. 5 | */ 6 | #define CATCH_CONFIG_RUNNER 7 | #define TESTTHAT_TEST_RUNNER 8 | #include 9 | -------------------------------------------------------------------------------- /cpp11test/src/test-sexp.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/list.hpp" 2 | 3 | #include 4 | 5 | context("sexp-C++") { 6 | test_that("sexp initializer lists work") { 7 | using namespace cpp11::literals; 8 | cpp11::writable::list out({ 9 | "int"_nm = {1, 2, 3}, 10 | "dbl"_nm = {1., 2., 3.}, 11 | "char"_nm = {"x", "y", "z"}, 12 | }); 13 | 14 | out.attr("class") = "data.frame"; 15 | out.attr("row.names") = {NA_INTEGER, -3}; 16 | 17 | expect_true(Rf_inherits(out, "data.frame")); 18 | } 19 | 20 | test_that("scalar constructors work") { 21 | using namespace cpp11::literals; 22 | cpp11::writable::list out({ 23 | "int"_nm = 1, 24 | "dbl"_nm = 1., 25 | "char"_nm = "x", 26 | }); 27 | 28 | out.attr("class") = "data.frame"; 29 | out.attr("row.names") = {NA_INTEGER, -1}; 30 | 31 | expect_true(Rf_inherits(out, "data.frame")); 32 | } 33 | 34 | test_that("move constructor works") { 35 | using namespace cpp11::literals; 36 | cpp11::sexp x(Rf_ScalarReal(5.)); 37 | cpp11::sexp y(std::move(x)); 38 | 39 | expect_true(SEXP(x) == R_NilValue); 40 | expect_true(REAL(y)[0] == 5.); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cpp11test/src/test-string.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/strings.hpp" 2 | 3 | #include 4 | 5 | context("string-C++") { 6 | test_that("is_na(string)") { 7 | cpp11::r_string x("foo"); 8 | expect_true(!cpp11::is_na(x)); 9 | 10 | cpp11::r_string y(NA_STRING); 11 | expect_true(cpp11::is_na(y)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cpp11test/src/truncate.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp11/doubles.hpp" 2 | 3 | [[cpp11::register]] SEXP cpp11_push_and_truncate_(SEXP size_sexp) { 4 | R_xlen_t size = INTEGER(size_sexp)[0]; 5 | 6 | // Allocate `size` worth of doubles (filled with garbage data) 7 | cpp11::writable::doubles out(size); 8 | 9 | // Push 1 more past the existing length/capacity, 10 | // doubling the capacity for cpp11 vectors 11 | out.push_back(0); 12 | 13 | // Truncate back to `size + 1` size and return result. 14 | return SEXP(out); 15 | } 16 | -------------------------------------------------------------------------------- /cpp11test/tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(cpp11) 3 | 4 | test_check("cpp11test") 5 | -------------------------------------------------------------------------------- /cpp11test/tests/testthat/test-cpp.R: -------------------------------------------------------------------------------- 1 | run_cpp_tests("cpp11test") 2 | -------------------------------------------------------------------------------- /cpp11test/tests/testthat/test-doubles.R: -------------------------------------------------------------------------------- 1 | test_that("doubles iterators work with normal vectors", { 2 | 3 | len <- 1e5 4 | set.seed(42) 5 | x <- rnorm(len) 6 | sum_base <- sum(x) 7 | 8 | expect_equal(sum_dbl_for_(x), sum_base) 9 | expect_equal(sum_dbl_foreach_(x), sum_base) 10 | expect_equal(sum_dbl_accumulate_(x), sum_base) 11 | expect_equal(sum_dbl_for2_(x), sum_base) 12 | }) 13 | 14 | test_that("doubles iterators work with altrep vectors", { 15 | 16 | len <- 1e5 17 | seq_double <- function(x) as.double(seq_len(x)) 18 | 19 | x <- seq_double(len) 20 | 21 | sum_base <- sum(x) 22 | 23 | expect_equal(sum_dbl_for_(x), sum_base) 24 | expect_equal(sum_dbl_foreach_(x), sum_base) 25 | expect_equal(sum_dbl_accumulate_(x), sum_base) 26 | expect_equal(sum_dbl_for2_(x), sum_base) 27 | }) 28 | 29 | test_that("writable::doubles grow", { 30 | len <- 1e5L 31 | expect_equal(grow_(len), as.numeric(seq(0, len - 1))) 32 | }) 33 | -------------------------------------------------------------------------------- /cpp11test/tests/testthat/test-matrix.R: -------------------------------------------------------------------------------- 1 | test_that("row_sums gives same result as rowSums", { 2 | x <- cbind(x1 = 3, x2 = c(4:1, 2:5)) 3 | expect_equal(row_sums(x), rowSums(x)) 4 | 5 | # With missing values 6 | x[4, 2] <- NA 7 | expect_equal(row_sums(x), rowSums(x)) 8 | 9 | y <- cbind(x1 = 3, x2 = c(4:1, 2:5)) 10 | y[3, ] <- NA; 11 | expect_equal(row_sums(y), rowSums(y)) 12 | }) 13 | 14 | test_that("col_sums gives same result as colSums", { 15 | x <- cbind(3, c(4:1, 2:5)) 16 | expect_equal(col_sums(x), colSums(x)) 17 | 18 | # With missing values 19 | x[4, 2] <- NA 20 | expect_equal(col_sums(x), colSums(x)) 21 | 22 | y <- cbind(3, c(4:1, 2:5)) 23 | y[3, ] <- NA; 24 | expect_equal(col_sums(y), colSums(y)) 25 | }) 26 | -------------------------------------------------------------------------------- /cpp11test/tests/testthat/test_formatted_errors.R: -------------------------------------------------------------------------------- 1 | test_that("cpp11::stop formatting works", { 2 | test1 <- 4 3 | expect_error(my_stop_n1("This is a stop"), "This is a stop", fixed = TRUE) 4 | expect_error(my_stop("Your number is {}", test1), "Your number is 4", fixed = TRUE) 5 | 6 | test2 <- c(3, 5, 7) 7 | expect_error(my_stop("You've tested this {} times", test2[1]), "You've tested this 3 times", 8 | fixed = TRUE) 9 | }) 10 | test_that("cpp11::warning formatting works", { 11 | test1 <- "warning" 12 | expect_warning(my_warning_n1("This is a warning"), "This is a warning", fixed = TRUE) 13 | expect_warning(my_warning("This is a {}", test1), "This is a warning", fixed = TRUE) 14 | 15 | test2 <- c("failed", "passed") 16 | expect_warning(my_warning("You {}", test2[2]), "You passed", fixed = TRUE) 17 | }) 18 | test_that("cpp11::message formatting works", { 19 | test1 <- "message" 20 | expect_message(my_message_n1("This is a message"), "This is a message", fixed = TRUE) 21 | expect_message(my_message("This is a {}", test1), "This is a message", fixed = TRUE) 22 | 23 | test2 <- c("great", "super") 24 | expect_message(my_message("You're {}", test2[2]), "You're super", fixed = TRUE) 25 | }) 26 | test_that("cpp11::stop works without including the fmt library", { 27 | test1 <- "error" 28 | expect_error(my_stop_n1fmt("This is a stop"), "This is a stop", fixed = TRUE) 29 | expect_error(my_stop_n2fmt("This is an %s", test1), "This is an error", fixed = TRUE) 30 | }) 31 | test_that("cpp11::warning works without including the fmt library", { 32 | test1 <- "warning" 33 | expect_warning(my_warning_n1fmt("This is a warning"), "This is a warning", fixed = TRUE) 34 | expect_warning(my_warning_n2fmt("This is a %s", test1), "This is a warning", fixed = TRUE) 35 | }) 36 | test_that("cpp11::message works without including the fmt library", { 37 | test1 <- "message" 38 | expect_message(my_message_n1fmt("This is a message"), "This is a message", fixed = TRUE) 39 | expect_message(my_message_n2fmt("This is a %s", test1), "This is a message", fixed = TRUE) 40 | 41 | test2 <- c("great", "super") 42 | expect_message(my_message_n2fmt("You're %s", test2[2]), "You're super", fixed = TRUE) 43 | }) 44 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | This is a release with no expected breakage of any reverse dependencies. 2 | -------------------------------------------------------------------------------- /inst/include/cpp11.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpp11/R.hpp" 4 | #include "cpp11/altrep.hpp" 5 | #include "cpp11/as.hpp" 6 | #include "cpp11/attribute_proxy.hpp" 7 | #include "cpp11/data_frame.hpp" 8 | #include "cpp11/doubles.hpp" 9 | #include "cpp11/environment.hpp" 10 | #include "cpp11/external_pointer.hpp" 11 | #include "cpp11/function.hpp" 12 | #include "cpp11/integers.hpp" 13 | #include "cpp11/list.hpp" 14 | #include "cpp11/list_of.hpp" 15 | #include "cpp11/logicals.hpp" 16 | #include "cpp11/matrix.hpp" 17 | #include "cpp11/named_arg.hpp" 18 | #include "cpp11/protect.hpp" 19 | #include "cpp11/r_bool.hpp" 20 | #include "cpp11/r_string.hpp" 21 | #include "cpp11/r_vector.hpp" 22 | #include "cpp11/raws.hpp" 23 | #include "cpp11/sexp.hpp" 24 | #include "cpp11/strings.hpp" 25 | -------------------------------------------------------------------------------- /inst/include/cpp11/R.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef R_INTERNALS_H_ 4 | #if !(defined(R_NO_REMAP) && defined(STRICT_R_HEADERS)) 5 | #error R headers were included before cpp11 headers \ 6 | and at least one of R_NO_REMAP or STRICT_R_HEADERS \ 7 | was not defined. 8 | #endif 9 | #endif 10 | 11 | #ifndef R_NO_REMAP 12 | #define R_NO_REMAP 13 | #endif 14 | 15 | #ifndef STRICT_R_HEADERS 16 | #define STRICT_R_HEADERS 17 | #endif 18 | 19 | #include "R_ext/Boolean.h" 20 | #include "Rinternals.h" 21 | #include "Rversion.h" 22 | 23 | // clang-format off 24 | #ifdef __clang__ 25 | # pragma clang diagnostic push 26 | # pragma clang diagnostic ignored "-Wattributes" 27 | #endif 28 | 29 | #ifdef __GNUC__ 30 | # pragma GCC diagnostic push 31 | # pragma GCC diagnostic ignored "-Wattributes" 32 | #endif 33 | // clang-format on 34 | 35 | #include 36 | 37 | #if defined(R_VERSION) && R_VERSION >= R_Version(4, 4, 0) 38 | // Use R's new macro 39 | #define CPP11_PRIdXLEN_T R_PRIdXLEN_T 40 | #else 41 | // Recreate what new R does 42 | #ifdef LONG_VECTOR_SUPPORT 43 | #define CPP11_PRIdXLEN_T "td" 44 | #else 45 | #define CPP11_PRIdXLEN_T "d" 46 | #endif 47 | #endif 48 | 49 | namespace cpp11 { 50 | namespace literals { 51 | 52 | constexpr R_xlen_t operator""_xl(unsigned long long int value) { return value; } 53 | 54 | } // namespace literals 55 | 56 | namespace traits { 57 | template 58 | struct get_underlying_type { 59 | using type = T; 60 | }; 61 | } // namespace traits 62 | 63 | namespace detail { 64 | 65 | // Annoyingly, `TYPEOF()` returns an `int` rather than a `SEXPTYPE`, 66 | // which can throw warnings with `-Wsign-compare` on Windows. 67 | inline SEXPTYPE r_typeof(SEXP x) { return static_cast(TYPEOF(x)); } 68 | 69 | /// Get an object from an environment 70 | /// 71 | /// SAFETY: Keep as a pure C function. Call like an R API function, i.e. wrap in `safe[]` 72 | /// as required. 73 | inline SEXP r_env_get(SEXP env, SEXP sym) { 74 | #if defined(R_VERSION) && R_VERSION >= R_Version(4, 5, 0) 75 | const Rboolean inherits = FALSE; 76 | return R_getVar(sym, env, inherits); 77 | #else 78 | SEXP out = Rf_findVarInFrame3(env, sym, TRUE); 79 | 80 | // Replicate the 3 checks from `R_getVar()` (along with exact error message): 81 | // - Object must be found in the `env` 82 | // - `R_MissingArg` can't leak from an `env` anymore 83 | // - Promises can't leak from an `env` anymore 84 | 85 | if (out == R_MissingArg) { 86 | Rf_errorcall(R_NilValue, "argument \"%s\" is missing, with no default", 87 | CHAR(PRINTNAME(sym))); 88 | } 89 | 90 | if (out == R_UnboundValue) { 91 | Rf_errorcall(R_NilValue, "object '%s' not found", CHAR(PRINTNAME(sym))); 92 | } 93 | 94 | if (r_typeof(out) == PROMSXP) { 95 | PROTECT(out); 96 | out = Rf_eval(out, env); 97 | UNPROTECT(1); 98 | } 99 | 100 | return out; 101 | #endif 102 | } 103 | 104 | /// Check if an object exists in an environment 105 | /// 106 | /// SAFETY: Keep as a pure C function. Call like an R API function, i.e. wrap in `safe[]` 107 | /// as required. 108 | inline bool r_env_has(SEXP env, SEXP sym) { 109 | #if R_VERSION >= R_Version(4, 2, 0) 110 | return R_existsVarInFrame(env, sym); 111 | #else 112 | return Rf_findVarInFrame3(env, sym, FALSE) != R_UnboundValue; 113 | #endif 114 | } 115 | 116 | } // namespace detail 117 | 118 | template 119 | inline T na(); 120 | 121 | template 122 | inline typename std::enable_if::type, double>::value, 123 | bool>::type 124 | is_na(const T& value) { 125 | return value == na(); 126 | } 127 | 128 | template 129 | inline typename std::enable_if::type, double>::value, 130 | bool>::type 131 | is_na(const T& value) { 132 | return ISNA(value); 133 | } 134 | 135 | } // namespace cpp11 136 | -------------------------------------------------------------------------------- /inst/include/cpp11/altrep.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // It would be nice to remove this since all supported versions of R have ALTREP, but 4 | // some groups rely on both this `#define` and `altrep.hpp` itself existing, like arrow: 5 | // https://github.com/r-lib/cpp11/issues/413 6 | #define HAS_ALTREP 7 | -------------------------------------------------------------------------------- /inst/include/cpp11/attribute_proxy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for initializer_list 4 | #include // for string, basic_string 5 | 6 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_install, PROTECT, Rf_... 7 | #include "cpp11/as.hpp" // for as_sexp 8 | #include "cpp11/protect.hpp" // for protect, safe, protect::function 9 | 10 | namespace cpp11 { 11 | 12 | class sexp; 13 | 14 | template 15 | class attribute_proxy { 16 | private: 17 | const T& parent_; 18 | SEXP symbol_; 19 | 20 | public: 21 | attribute_proxy(const T& parent, const char* index) 22 | : parent_(parent), symbol_(safe[Rf_install](index)) {} 23 | 24 | attribute_proxy(const T& parent, const std::string& index) 25 | : parent_(parent), symbol_(safe[Rf_install](index.c_str())) {} 26 | 27 | attribute_proxy(const T& parent, SEXP index) : parent_(parent), symbol_(index) {} 28 | 29 | template 30 | attribute_proxy& operator=(C rhs) { 31 | SEXP value = PROTECT(as_sexp(rhs)); 32 | Rf_setAttrib(parent_.data(), symbol_, value); 33 | UNPROTECT(1); 34 | return *this; 35 | } 36 | 37 | template 38 | attribute_proxy& operator=(std::initializer_list rhs) { 39 | SEXP value = PROTECT(as_sexp(rhs)); 40 | Rf_setAttrib(parent_.data(), symbol_, value); 41 | UNPROTECT(1); 42 | return *this; 43 | } 44 | 45 | operator SEXP() const { return safe[Rf_getAttrib](parent_.data(), symbol_); } 46 | }; 47 | 48 | } // namespace cpp11 49 | -------------------------------------------------------------------------------- /inst/include/cpp11/data_frame.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for abs 4 | #include 5 | #include // for initializer_list 6 | #include // for string, basic_string 7 | #include // for move 8 | 9 | #include "R_ext/Arith.h" // for NA_INTEGER 10 | #include "cpp11/R.hpp" // for Rf_xlength, SEXP, SEXPREC, INTEGER 11 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 12 | #include "cpp11/list.hpp" // for list, r_vector<>::r_vector, r_v... 13 | #include "cpp11/r_vector.hpp" // for r_vector 14 | 15 | namespace cpp11 { 16 | 17 | class named_arg; 18 | namespace writable { 19 | class data_frame; 20 | } // namespace writable 21 | 22 | class data_frame : public list { 23 | using list::list; 24 | 25 | friend class writable::data_frame; 26 | 27 | /* we cannot use Rf_getAttrib because it has a special case for c(NA, -n) and creates 28 | * the full vector */ 29 | static SEXP get_attrib0(SEXP x, SEXP sym) { 30 | for (SEXP attr = ATTRIB(x); attr != R_NilValue; attr = CDR(attr)) { 31 | if (TAG(attr) == sym) { 32 | return CAR(attr); 33 | } 34 | } 35 | 36 | return R_NilValue; 37 | } 38 | 39 | static R_xlen_t calc_nrow(SEXP x) { 40 | auto nms = get_attrib0(x, R_RowNamesSymbol); 41 | bool has_short_rownames = 42 | (Rf_isInteger(nms) && Rf_xlength(nms) == 2 && INTEGER(nms)[0] == NA_INTEGER); 43 | if (has_short_rownames) { 44 | return static_cast(abs(INTEGER(nms)[1])); 45 | } 46 | 47 | if (!Rf_isNull(nms)) { 48 | return Rf_xlength(nms); 49 | } 50 | 51 | if (Rf_xlength(x) == 0) { 52 | return 0; 53 | } 54 | 55 | return Rf_xlength(VECTOR_ELT(x, 0)); 56 | } 57 | 58 | public: 59 | /* Adapted from 60 | * https://github.com/wch/r-source/blob/f2a0dfab3e26fb42b8b296fcba40cbdbdbec767d/src/main/attrib.c#L198-L207 61 | */ 62 | R_xlen_t nrow() const { return calc_nrow(*this); } 63 | R_xlen_t ncol() const { return size(); } 64 | }; 65 | 66 | namespace writable { 67 | class data_frame : public cpp11::data_frame { 68 | private: 69 | writable::list set_data_frame_attributes(writable::list&& x) { 70 | return set_data_frame_attributes(std::move(x), calc_nrow(x)); 71 | } 72 | 73 | writable::list set_data_frame_attributes(writable::list&& x, R_xlen_t nrow) { 74 | x.attr(R_RowNamesSymbol) = {NA_INTEGER, -static_cast(nrow)}; 75 | x.attr(R_ClassSymbol) = "data.frame"; 76 | return std::move(x); 77 | } 78 | 79 | public: 80 | data_frame(const SEXP data) : cpp11::data_frame(set_data_frame_attributes(data)) {} 81 | data_frame(const SEXP data, bool is_altrep) 82 | : cpp11::data_frame(set_data_frame_attributes(data), is_altrep) {} 83 | data_frame(const SEXP data, bool is_altrep, R_xlen_t nrow) 84 | : cpp11::data_frame(set_data_frame_attributes(data, nrow), is_altrep) {} 85 | data_frame(std::initializer_list il) 86 | : cpp11::data_frame(set_data_frame_attributes(writable::list(il))) {} 87 | data_frame(std::initializer_list il) 88 | : cpp11::data_frame(set_data_frame_attributes(writable::list(il))) {} 89 | 90 | using cpp11::data_frame::ncol; 91 | using cpp11::data_frame::nrow; 92 | 93 | attribute_proxy attr(const char* name) const { return {*this, name}; } 94 | 95 | attribute_proxy attr(const std::string& name) const { 96 | return {*this, name.c_str()}; 97 | } 98 | 99 | attribute_proxy attr(SEXP name) const { return {*this, name}; } 100 | 101 | attribute_proxy names() const { return {*this, R_NamesSymbol}; } 102 | }; 103 | 104 | } // namespace writable 105 | 106 | } // namespace cpp11 107 | -------------------------------------------------------------------------------- /inst/include/cpp11/declarations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Davis: From what I can tell, you'd only ever define this if you need to include 8 | // `declarations.hpp` manually in a file, i.e. to possibly use `BEGIN_CPP11` with a 9 | // custom `END_CPP11`, as textshaping does do. Otherwise, `declarations.hpp` is included 10 | // in `code.cpp` and should contain all of the cpp11 type definitions that the generated 11 | // function signatures need to link against. 12 | #ifndef CPP11_PARTIAL 13 | #include "cpp11.hpp" 14 | namespace writable = ::cpp11::writable; 15 | using namespace ::cpp11; 16 | #endif 17 | 18 | #include 19 | 20 | namespace cpp11 { 21 | // No longer used, but was previously used in `code.cpp` code generation in cpp11 0.1.0. 22 | // `code.cpp` could be generated with cpp11 0.1.0, but the package could be compiled with 23 | // cpp11 >0.1.0, so `unmove()` must exist in newer cpp11 too. Eventually remove this once 24 | // we decide enough time has gone by since `unmove()` was removed. 25 | // https://github.com/r-lib/cpp11/issues/88 26 | // https://github.com/r-lib/cpp11/pull/75 27 | template 28 | T& unmove(T&& t) { 29 | return t; 30 | } 31 | } // namespace cpp11 32 | 33 | // We would like to remove this, since all supported versions of R now support proper 34 | // unwind protect, but some groups rely on it existing, like textshaping: 35 | // https://github.com/r-lib/cpp11/issues/414 36 | #define CPP11_UNWIND R_ContinueUnwind(err); 37 | 38 | #define CPP11_ERROR_BUFSIZE 8192 39 | 40 | #define BEGIN_CPP11 \ 41 | SEXP err = R_NilValue; \ 42 | char buf[CPP11_ERROR_BUFSIZE] = ""; \ 43 | try { 44 | #define END_CPP11 \ 45 | } \ 46 | catch (cpp11::unwind_exception & e) { \ 47 | err = e.token; \ 48 | } \ 49 | catch (std::exception & e) { \ 50 | strncpy(buf, e.what(), sizeof(buf) - 1); \ 51 | } \ 52 | catch (...) { \ 53 | strncpy(buf, "C++ error (unknown cause)", sizeof(buf) - 1); \ 54 | } \ 55 | if (buf[0] != '\0') { \ 56 | Rf_errorcall(R_NilValue, "%s", buf); \ 57 | } else if (err != R_NilValue) { \ 58 | R_ContinueUnwind(err); \ 59 | } \ 60 | return R_NilValue; 61 | -------------------------------------------------------------------------------- /inst/include/cpp11/doubles.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for min, tranform 4 | #include // for array 5 | #include // for initializer_list 6 | 7 | #include "R_ext/Arith.h" // for ISNA 8 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector, REAL 9 | #include "cpp11/as.hpp" // for as_sexp 10 | #include "cpp11/protect.hpp" // for safe 11 | #include "cpp11/r_vector.hpp" // for vector, vector<>::proxy, vector<>::... 12 | #include "cpp11/sexp.hpp" // for sexp 13 | 14 | // Specializations for doubles 15 | 16 | namespace cpp11 { 17 | 18 | template <> 19 | inline SEXPTYPE r_vector::get_sexptype() { 20 | return REALSXP; 21 | } 22 | 23 | template <> 24 | inline typename r_vector::underlying_type r_vector::get_elt(SEXP x, 25 | R_xlen_t i) { 26 | // NOPROTECT: likely too costly to unwind protect every elt 27 | return REAL_ELT(x, i); 28 | } 29 | 30 | template <> 31 | inline typename r_vector::underlying_type* r_vector::get_p(bool is_altrep, 32 | SEXP data) { 33 | if (is_altrep) { 34 | return nullptr; 35 | } else { 36 | return REAL(data); 37 | } 38 | } 39 | 40 | template <> 41 | inline typename r_vector::underlying_type const* r_vector::get_const_p( 42 | bool is_altrep, SEXP data) { 43 | return REAL_OR_NULL(data); 44 | } 45 | 46 | template <> 47 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 48 | typename r_vector::underlying_type* buf) { 49 | // NOPROTECT: likely too costly to unwind protect here 50 | REAL_GET_REGION(x, i, n, buf); 51 | } 52 | 53 | template <> 54 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 55 | return is_altrep; 56 | } 57 | 58 | typedef r_vector doubles; 59 | 60 | namespace writable { 61 | 62 | template <> 63 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 64 | typename r_vector::underlying_type value) { 65 | // NOPROTECT: Likely too costly to unwind protect every set elt 66 | SET_REAL_ELT(x, i, value); 67 | } 68 | 69 | typedef r_vector doubles; 70 | 71 | } // namespace writable 72 | 73 | typedef r_vector integers; 74 | 75 | inline doubles as_doubles(SEXP x) { 76 | if (detail::r_typeof(x) == REALSXP) { 77 | return doubles(x); 78 | } 79 | 80 | else if (detail::r_typeof(x) == INTSXP) { 81 | integers xn(x); 82 | size_t len = xn.size(); 83 | writable::doubles ret(len); 84 | std::transform(xn.begin(), xn.end(), ret.begin(), [](int value) { 85 | return value == NA_INTEGER ? NA_REAL : static_cast(value); 86 | }); 87 | return ret; 88 | } 89 | 90 | throw type_error(REALSXP, detail::r_typeof(x)); 91 | } 92 | 93 | template <> 94 | inline double na() { 95 | return NA_REAL; 96 | } 97 | 98 | } // namespace cpp11 99 | -------------------------------------------------------------------------------- /inst/include/cpp11/environment.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for string, basic_string 4 | 5 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_install, r_env_get... 6 | #include "cpp11/as.hpp" // for as_sexp 7 | #include "cpp11/protect.hpp" // for protect, protect::function, safe, unwin... 8 | #include "cpp11/sexp.hpp" // for sexp 9 | 10 | namespace cpp11 { 11 | 12 | class environment { 13 | private: 14 | sexp env_; 15 | 16 | class proxy { 17 | SEXP parent_; 18 | SEXP name_; 19 | 20 | public: 21 | proxy(SEXP parent, SEXP name) : parent_(parent), name_(name) {} 22 | 23 | template 24 | proxy& operator=(T value) { 25 | safe[Rf_defineVar](name_, as_sexp(value), parent_); 26 | return *this; 27 | } 28 | operator SEXP() const { return safe[detail::r_env_get](parent_, name_); }; 29 | operator sexp() const { return SEXP(); }; 30 | }; 31 | 32 | public: 33 | environment(SEXP env) : env_(env) {} 34 | environment(sexp env) : env_(env) {} 35 | proxy operator[](const SEXP name) const { return {env_, name}; } 36 | proxy operator[](const char* name) const { return operator[](safe[Rf_install](name)); } 37 | proxy operator[](const std::string& name) const { return operator[](name.c_str()); } 38 | 39 | bool exists(SEXP name) const { return safe[detail::r_env_has](env_, name); } 40 | bool exists(const char* name) const { return exists(safe[Rf_install](name)); } 41 | bool exists(const std::string& name) const { return exists(name.c_str()); } 42 | 43 | void remove(SEXP name) { 44 | PROTECT(name); 45 | R_removeVarFromFrame(name, env_); 46 | UNPROTECT(1); 47 | } 48 | 49 | void remove(const char* name) { remove(safe[Rf_install](name)); } 50 | 51 | R_xlen_t size() const { return Rf_xlength(env_); } 52 | 53 | operator SEXP() const { return env_; } 54 | }; 55 | 56 | } // namespace cpp11 57 | -------------------------------------------------------------------------------- /inst/include/cpp11/external_pointer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for nullptr_t, NULL 4 | #include // for bad_weak_ptr 5 | #include // for add_lvalue_reference 6 | 7 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, R_NilValue 8 | #include "cpp11/protect.hpp" // for protect, safe, protect::function 9 | #include "cpp11/r_bool.hpp" // for r_bool 10 | #include "cpp11/r_vector.hpp" // for type_error 11 | #include "cpp11/sexp.hpp" // for sexp 12 | 13 | namespace cpp11 { 14 | 15 | template 16 | void default_deleter(T* obj) { 17 | delete obj; 18 | } 19 | 20 | template > 21 | class external_pointer { 22 | private: 23 | sexp data_ = R_NilValue; 24 | 25 | static SEXP valid_type(SEXP data) { 26 | if (data == nullptr) { 27 | throw type_error(EXTPTRSXP, NILSXP); 28 | } 29 | if (detail::r_typeof(data) != EXTPTRSXP) { 30 | throw type_error(EXTPTRSXP, detail::r_typeof(data)); 31 | } 32 | 33 | return data; 34 | } 35 | 36 | static void r_deleter(SEXP p) { 37 | if (detail::r_typeof(p) != EXTPTRSXP) return; 38 | 39 | T* ptr = static_cast(R_ExternalPtrAddr(p)); 40 | 41 | if (ptr == NULL) { 42 | return; 43 | } 44 | 45 | R_ClearExternalPtr(p); 46 | 47 | Deleter(ptr); 48 | } 49 | 50 | public: 51 | using pointer = T*; 52 | 53 | external_pointer() noexcept {} 54 | external_pointer(std::nullptr_t) noexcept {} 55 | 56 | external_pointer(SEXP data) : data_(valid_type(data)) {} 57 | 58 | external_pointer(pointer p, bool use_deleter = true, bool finalize_on_exit = true) 59 | : data_(safe[R_MakeExternalPtr]((void*)p, R_NilValue, R_NilValue)) { 60 | if (use_deleter) { 61 | R_RegisterCFinalizerEx(data_, r_deleter, static_cast(finalize_on_exit)); 62 | } 63 | } 64 | 65 | external_pointer(const external_pointer& rhs) { 66 | data_ = safe[Rf_shallow_duplicate](rhs.data_); 67 | } 68 | 69 | external_pointer(external_pointer&& rhs) { reset(rhs.release()); } 70 | 71 | external_pointer& operator=(external_pointer&& rhs) noexcept { reset(rhs.release()); } 72 | 73 | external_pointer& operator=(std::nullptr_t) noexcept { reset(); }; 74 | 75 | operator SEXP() const noexcept { return data_; } 76 | 77 | pointer get() const noexcept { 78 | pointer addr = static_cast(R_ExternalPtrAddr(data_)); 79 | if (addr == nullptr) { 80 | return nullptr; 81 | } 82 | return addr; 83 | } 84 | 85 | typename std::add_lvalue_reference::type operator*() { 86 | pointer addr = get(); 87 | if (addr == nullptr) { 88 | throw std::bad_weak_ptr(); 89 | } 90 | return *get(); 91 | } 92 | 93 | pointer operator->() const { 94 | pointer addr = get(); 95 | if (addr == nullptr) { 96 | throw std::bad_weak_ptr(); 97 | } 98 | return get(); 99 | } 100 | 101 | pointer release() noexcept { 102 | if (get() == nullptr) { 103 | return nullptr; 104 | } 105 | pointer ptr = get(); 106 | R_ClearExternalPtr(data_); 107 | 108 | return ptr; 109 | } 110 | 111 | void reset(pointer ptr = pointer()) { 112 | SEXP old_data = data_; 113 | data_ = safe[R_MakeExternalPtr]((void*)ptr, R_NilValue, R_NilValue); 114 | r_deleter(old_data); 115 | } 116 | 117 | void swap(external_pointer& other) noexcept { 118 | SEXP tmp = other.data_; 119 | other.data_ = data_; 120 | data_ = tmp; 121 | } 122 | 123 | operator bool() noexcept { return data_ != nullptr; } 124 | }; 125 | 126 | template 127 | void swap(external_pointer& lhs, external_pointer& rhs) noexcept { 128 | lhs.swap(rhs); 129 | } 130 | 131 | template 132 | bool operator==(const external_pointer& x, 133 | const external_pointer& y) { 134 | return x.data_ == y.data_; 135 | } 136 | 137 | template 138 | bool operator!=(const external_pointer& x, 139 | const external_pointer& y) { 140 | return x.data_ != y.data_; 141 | } 142 | 143 | template 144 | bool operator<(const external_pointer& x, 145 | const external_pointer& y) { 146 | return x.data_ < y.data_; 147 | } 148 | 149 | template 150 | bool operator<=(const external_pointer& x, 151 | const external_pointer& y) { 152 | return x.data_ <= y.data_; 153 | } 154 | 155 | template 156 | bool operator>(const external_pointer& x, 157 | const external_pointer& y) { 158 | return x.data_ > y.data_; 159 | } 160 | 161 | template 162 | bool operator>=(const external_pointer& x, 163 | const external_pointer& y) { 164 | return x.data_ >= y.data_; 165 | } 166 | 167 | } // namespace cpp11 168 | -------------------------------------------------------------------------------- /inst/include/cpp11/function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for strcmp 4 | 5 | #include // for snprintf 6 | #include // for string, basic_string 7 | #include // for forward 8 | 9 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, CDR, Rf_install, SETCAR 10 | #include "cpp11/as.hpp" // for as_sexp 11 | #include "cpp11/named_arg.hpp" // for named_arg 12 | #include "cpp11/protect.hpp" // for protect, protect::function, safe 13 | #include "cpp11/sexp.hpp" // for sexp 14 | 15 | namespace cpp11 { 16 | 17 | class function { 18 | public: 19 | function(SEXP data) : data_(data) {} 20 | 21 | template 22 | sexp operator()(Args&&... args) const { 23 | // Size of the arguments plus one for the function name itself 24 | R_xlen_t num_args = sizeof...(args) + 1; 25 | 26 | sexp call(safe[Rf_allocVector](LANGSXP, num_args)); 27 | 28 | construct_call(call, data_, std::forward(args)...); 29 | 30 | return safe[Rf_eval](call, R_GlobalEnv); 31 | } 32 | 33 | private: 34 | sexp data_; 35 | 36 | template 37 | void construct_call(SEXP val, const named_arg& arg, Args&&... args) const { 38 | SETCAR(val, arg.value()); 39 | SET_TAG(val, safe[Rf_install](arg.name())); 40 | val = CDR(val); 41 | construct_call(val, std::forward(args)...); 42 | } 43 | 44 | // Construct the call recursively, each iteration adds an Arg to the pairlist. 45 | template 46 | void construct_call(SEXP val, const T& arg, Args&&... args) const { 47 | SETCAR(val, as_sexp(arg)); 48 | val = CDR(val); 49 | construct_call(val, std::forward(args)...); 50 | } 51 | 52 | // Base case, just return 53 | void construct_call(SEXP val) const {} 54 | }; 55 | 56 | class package { 57 | public: 58 | package(const char* name) : data_(get_namespace(name)) {} 59 | package(const std::string& name) : data_(get_namespace(name.c_str())) {} 60 | function operator[](const char* name) { 61 | return safe[Rf_findFun](safe[Rf_install](name), data_); 62 | } 63 | function operator[](const std::string& name) { return operator[](name.c_str()); } 64 | 65 | private: 66 | static SEXP get_namespace(const char* name) { 67 | if (strcmp(name, "base") == 0) { 68 | return R_BaseEnv; 69 | } 70 | sexp name_sexp = safe[Rf_install](name); 71 | return safe[detail::r_env_get](R_NamespaceRegistry, name_sexp); 72 | } 73 | 74 | // Either base env or in namespace registry, so no protection needed 75 | SEXP data_; 76 | }; 77 | 78 | namespace detail { 79 | 80 | // Special internal way to call `base::message()` 81 | // 82 | // - Pure C, so call with `safe[]` 83 | // - Holds a `static SEXP` for the `base::message` function protected with 84 | // `R_PreserveObject()` 85 | // 86 | // We don't use a `static cpp11::function` because that will infinitely retain a cell in 87 | // our preserve list, which can throw off our counts in the preserve list tests. 88 | inline void r_message(const char* x) { 89 | static SEXP fn = NULL; 90 | 91 | if (fn == NULL) { 92 | fn = Rf_findFun(Rf_install("message"), R_BaseEnv); 93 | R_PreserveObject(fn); 94 | } 95 | 96 | SEXP x_char = PROTECT(Rf_mkCharCE(x, CE_UTF8)); 97 | SEXP x_string = PROTECT(Rf_ScalarString(x_char)); 98 | 99 | SEXP call = PROTECT(Rf_lang2(fn, x_string)); 100 | 101 | Rf_eval(call, R_GlobalEnv); 102 | 103 | UNPROTECT(3); 104 | } 105 | 106 | } // namespace detail 107 | 108 | inline void message(const char* fmt_arg) { 109 | #ifdef CPP11_USE_FMT 110 | std::string msg = fmt::format(fmt_arg); 111 | safe[detail::r_message](msg.c_str()); 112 | #else 113 | char buff[1024]; 114 | int msg; 115 | msg = std::snprintf(buff, 1024, "%s", fmt_arg); 116 | if (msg >= 0 && msg < 1024) { 117 | safe[detail::r_message](buff); 118 | } 119 | #endif 120 | } 121 | 122 | template 123 | void message(const char* fmt_arg, Args... args) { 124 | #ifdef CPP11_USE_FMT 125 | std::string msg = fmt::format(fmt_arg, args...); 126 | safe[detail::r_message](msg.c_str()); 127 | #else 128 | char buff[1024]; 129 | int msg; 130 | msg = std::snprintf(buff, 1024, fmt_arg, args...); 131 | if (msg >= 0 && msg < 1024) { 132 | safe[detail::r_message](buff); 133 | } 134 | #endif 135 | } 136 | 137 | inline void message(const std::string& fmt_arg) { message(fmt_arg.c_str()); } 138 | 139 | template 140 | void message(const std::string& fmt_arg, Args... args) { 141 | message(fmt_arg.c_str(), args...); 142 | } 143 | 144 | } // namespace cpp11 145 | -------------------------------------------------------------------------------- /inst/include/cpp11/integers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for min 4 | #include // for array 5 | #include // for initializer_list 6 | 7 | #include "R_ext/Arith.h" // for NA_INTEGER 8 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector 9 | #include "cpp11/as.hpp" // for as_sexp 10 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 11 | #include "cpp11/protect.hpp" // for safe 12 | #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy 13 | #include "cpp11/sexp.hpp" // for sexp 14 | 15 | // Specializations for integers 16 | 17 | namespace cpp11 { 18 | 19 | template <> 20 | inline SEXPTYPE r_vector::get_sexptype() { 21 | return INTSXP; 22 | } 23 | 24 | template <> 25 | inline typename r_vector::underlying_type r_vector::get_elt(SEXP x, 26 | R_xlen_t i) { 27 | // NOPROTECT: likely too costly to unwind protect every elt 28 | return INTEGER_ELT(x, i); 29 | } 30 | 31 | template <> 32 | inline typename r_vector::underlying_type* r_vector::get_p(bool is_altrep, 33 | SEXP data) { 34 | if (is_altrep) { 35 | return nullptr; 36 | } else { 37 | return INTEGER(data); 38 | } 39 | } 40 | 41 | template <> 42 | inline typename r_vector::underlying_type const* r_vector::get_const_p( 43 | bool is_altrep, SEXP data) { 44 | return INTEGER_OR_NULL(data); 45 | } 46 | 47 | template <> 48 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 49 | typename r_vector::underlying_type* buf) { 50 | // NOPROTECT: likely too costly to unwind protect here 51 | INTEGER_GET_REGION(x, i, n, buf); 52 | } 53 | 54 | template <> 55 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 56 | return is_altrep; 57 | } 58 | 59 | typedef r_vector integers; 60 | 61 | namespace writable { 62 | 63 | template <> 64 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 65 | typename r_vector::underlying_type value) { 66 | // NOPROTECT: Likely too costly to unwind protect every set elt 67 | SET_INTEGER_ELT(x, i, value); 68 | } 69 | 70 | typedef r_vector integers; 71 | 72 | } // namespace writable 73 | 74 | template <> 75 | inline int na() { 76 | return NA_INTEGER; 77 | } 78 | 79 | // forward declaration 80 | 81 | typedef r_vector doubles; 82 | 83 | inline integers as_integers(SEXP x) { 84 | if (detail::r_typeof(x) == INTSXP) { 85 | return integers(x); 86 | } else if (detail::r_typeof(x) == REALSXP) { 87 | doubles xn(x); 88 | writable::integers ret(xn.size()); 89 | std::transform(xn.begin(), xn.end(), ret.begin(), [](double value) { 90 | if (ISNA(value)) { 91 | return NA_INTEGER; 92 | } 93 | if (!is_convertible_without_loss_to_integer(value)) { 94 | throw std::runtime_error("All elements must be integer-like"); 95 | } 96 | return static_cast(value); 97 | }); 98 | return ret; 99 | } 100 | 101 | throw type_error(INTSXP, detail::r_typeof(x)); 102 | } 103 | 104 | } // namespace cpp11 105 | -------------------------------------------------------------------------------- /inst/include/cpp11/list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for initializer_list 4 | 5 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, SET_VECTOR_ELT 6 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 7 | #include "cpp11/protect.hpp" // for safe 8 | #include "cpp11/r_string.hpp" // for r_string 9 | #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy 10 | #include "cpp11/sexp.hpp" // for sexp 11 | 12 | // Specializations for list 13 | 14 | namespace cpp11 { 15 | 16 | template <> 17 | inline SEXPTYPE r_vector::get_sexptype() { 18 | return VECSXP; 19 | } 20 | 21 | template <> 22 | inline typename r_vector::underlying_type r_vector::get_elt(SEXP x, 23 | R_xlen_t i) { 24 | // NOPROTECT: likely too costly to unwind protect every elt 25 | return VECTOR_ELT(x, i); 26 | } 27 | 28 | template <> 29 | inline typename r_vector::underlying_type* r_vector::get_p(bool, SEXP) { 30 | return nullptr; 31 | } 32 | 33 | template <> 34 | inline typename r_vector::underlying_type const* r_vector::get_const_p( 35 | bool is_altrep, SEXP data) { 36 | // No `VECTOR_PTR_OR_NULL()` 37 | if (is_altrep) { 38 | return nullptr; 39 | } else { 40 | // TODO: Use `VECTOR_PTR_RO()` conditionally once R 4.5.0 is officially released 41 | return static_cast(DATAPTR_RO(data)); 42 | } 43 | } 44 | 45 | /// Specialization for lists, where `x["oob"]` returns `R_NilValue`, like at the R level 46 | template <> 47 | inline SEXP r_vector::get_oob() { 48 | return R_NilValue; 49 | } 50 | 51 | template <> 52 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 53 | typename r_vector::underlying_type* buf) { 54 | cpp11::stop("Unreachable!"); 55 | } 56 | 57 | template <> 58 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 59 | return false; 60 | } 61 | 62 | typedef r_vector list; 63 | 64 | namespace writable { 65 | 66 | template <> 67 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 68 | typename r_vector::underlying_type value) { 69 | // NOPROTECT: Likely too costly to unwind protect every set elt 70 | SET_VECTOR_ELT(x, i, value); 71 | } 72 | 73 | // Requires specialization to handle the fact that, for lists, each element of the 74 | // initializer list is considered the scalar "element", i.e. we don't expect that 75 | // each `named_arg` contains a list of length 1, like we do for the other vector types. 76 | // This means we don't need type checks, length 1 checks, or `get_elt()` for lists. 77 | template <> 78 | inline r_vector::r_vector(std::initializer_list il) 79 | : cpp11::r_vector(safe[Rf_allocVector](VECSXP, il.size())), 80 | capacity_(il.size()) { 81 | unwind_protect([&] { 82 | SEXP names = Rf_allocVector(STRSXP, capacity_); 83 | Rf_setAttrib(data_, R_NamesSymbol, names); 84 | 85 | auto it = il.begin(); 86 | 87 | for (R_xlen_t i = 0; i < capacity_; ++i, ++it) { 88 | SEXP elt = it->value(); 89 | set_elt(data_, i, elt); 90 | 91 | SEXP name = Rf_mkCharCE(it->name(), CE_UTF8); 92 | SET_STRING_ELT(names, i, name); 93 | } 94 | }); 95 | } 96 | 97 | typedef r_vector list; 98 | 99 | } // namespace writable 100 | 101 | } // namespace cpp11 102 | -------------------------------------------------------------------------------- /inst/include/cpp11/list_of.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for string, basic_string 4 | 5 | #include "cpp11/R.hpp" // for R_xlen_t, SEXP, SEXPREC, LONG_VECTOR_SUPPORT 6 | #include "cpp11/list.hpp" // for list 7 | 8 | namespace cpp11 { 9 | 10 | template 11 | class list_of : public list { 12 | public: 13 | list_of(const list& data) : list(data) {} 14 | 15 | #ifdef LONG_VECTOR_SUPPORT 16 | T operator[](const int pos) const { return operator[](static_cast(pos)); } 17 | #endif 18 | 19 | T operator[](const R_xlen_t pos) const { return list::operator[](pos); } 20 | 21 | T operator[](const char* pos) const { return list::operator[](pos); } 22 | 23 | T operator[](const std::string& pos) const { return list::operator[](pos.c_str()); } 24 | }; 25 | 26 | namespace writable { 27 | template 28 | class list_of : public writable::list { 29 | public: 30 | list_of(const list& data) : writable::list(data) {} 31 | list_of(R_xlen_t n) : writable::list(n) {} 32 | 33 | class proxy { 34 | private: 35 | writable::list::proxy data_; 36 | 37 | public: 38 | proxy(const writable::list::proxy& data) : data_(data) {} 39 | 40 | operator T() const { return static_cast(*this); } 41 | operator SEXP() const { return static_cast(data_); } 42 | #ifdef LONG_VECTOR_SUPPORT 43 | typename T::proxy operator[](int pos) { return static_cast(data_)[pos]; } 44 | #endif 45 | typename T::proxy operator[](R_xlen_t pos) { return static_cast(data_)[pos]; } 46 | proxy operator[](const char* pos) { static_cast(data_)[pos]; } 47 | proxy operator[](const std::string& pos) { return static_cast(data_)[pos]; } 48 | proxy& operator=(const T& rhs) { 49 | data_ = rhs; 50 | 51 | return *this; 52 | } 53 | }; 54 | 55 | #ifdef LONG_VECTOR_SUPPORT 56 | proxy operator[](int pos) { 57 | return {writable::list::operator[](static_cast(pos))}; 58 | } 59 | #endif 60 | 61 | proxy operator[](R_xlen_t pos) { return writable::list::operator[](pos); } 62 | 63 | proxy operator[](const char* pos) { return {writable::list::operator[](pos)}; } 64 | 65 | proxy operator[](const std::string& pos) { 66 | return writable::list::operator[](pos.c_str()); 67 | } 68 | }; 69 | } // namespace writable 70 | 71 | } // namespace cpp11 72 | -------------------------------------------------------------------------------- /inst/include/cpp11/logicals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for min 4 | #include // for array 5 | #include // for initializer_list 6 | 7 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_all... 8 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 9 | #include "cpp11/protect.hpp" // for safe 10 | #include "cpp11/r_bool.hpp" // for r_bool 11 | #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy 12 | #include "cpp11/sexp.hpp" // for sexp 13 | 14 | // Specializations for logicals 15 | 16 | namespace cpp11 { 17 | 18 | template <> 19 | inline SEXPTYPE r_vector::get_sexptype() { 20 | return LGLSXP; 21 | } 22 | 23 | template <> 24 | inline typename r_vector::underlying_type r_vector::get_elt(SEXP x, 25 | R_xlen_t i) { 26 | // NOPROTECT: likely too costly to unwind protect every elt 27 | return LOGICAL_ELT(x, i); 28 | } 29 | 30 | template <> 31 | inline typename r_vector::underlying_type* r_vector::get_p(bool is_altrep, 32 | SEXP data) { 33 | if (is_altrep) { 34 | return nullptr; 35 | } else { 36 | return LOGICAL(data); 37 | } 38 | } 39 | 40 | template <> 41 | inline typename r_vector::underlying_type const* r_vector::get_const_p( 42 | bool is_altrep, SEXP data) { 43 | return LOGICAL_OR_NULL(data); 44 | } 45 | 46 | template <> 47 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 48 | typename r_vector::underlying_type* buf) { 49 | // NOPROTECT: likely too costly to unwind protect here 50 | LOGICAL_GET_REGION(x, i, n, buf); 51 | } 52 | 53 | template <> 54 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 55 | return is_altrep; 56 | } 57 | 58 | typedef r_vector logicals; 59 | 60 | namespace writable { 61 | 62 | template <> 63 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 64 | typename r_vector::underlying_type value) { 65 | // NOPROTECT: Likely too costly to unwind protect every set elt 66 | SET_LOGICAL_ELT(x, i, value); 67 | } 68 | 69 | inline bool operator==(const r_vector::proxy& lhs, r_bool rhs) { 70 | return static_cast(lhs).operator==(rhs); 71 | } 72 | 73 | typedef r_vector logicals; 74 | 75 | } // namespace writable 76 | 77 | } // namespace cpp11 78 | -------------------------------------------------------------------------------- /inst/include/cpp11/named_arg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for size_t 4 | 5 | #include // for initializer_list 6 | 7 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, literals 8 | #include "cpp11/as.hpp" // for as_sexp 9 | #include "cpp11/sexp.hpp" // for sexp 10 | 11 | namespace cpp11 { 12 | class named_arg { 13 | public: 14 | explicit named_arg(const char* name) : name_(name), value_(R_NilValue) {} 15 | named_arg& operator=(std::initializer_list il) { 16 | value_ = as_sexp(il); 17 | return *this; 18 | } 19 | 20 | template 21 | named_arg& operator=(T rhs) { 22 | value_ = as_sexp(rhs); 23 | return *this; 24 | } 25 | 26 | template 27 | named_arg& operator=(std::initializer_list rhs) { 28 | value_ = as_sexp(rhs); 29 | return *this; 30 | } 31 | 32 | const char* name() const { return name_; } 33 | SEXP value() const { return value_; } 34 | 35 | private: 36 | const char* name_; 37 | sexp value_; 38 | }; 39 | 40 | namespace literals { 41 | 42 | inline named_arg operator""_nm(const char* name, std::size_t) { return named_arg(name); } 43 | 44 | } // namespace literals 45 | 46 | using namespace literals; 47 | 48 | } // namespace cpp11 49 | -------------------------------------------------------------------------------- /inst/include/cpp11/r_bool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for numeric_limits 4 | #include 5 | #include // for is_convertible, enable_if 6 | 7 | #include "R_ext/Boolean.h" // for Rboolean 8 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, ... 9 | #include "cpp11/as.hpp" // for as_sexp 10 | #include "cpp11/protect.hpp" // for unwind_protect 11 | #include "cpp11/r_vector.hpp" 12 | #include "cpp11/sexp.hpp" // for sexp 13 | 14 | namespace cpp11 { 15 | 16 | class r_bool { 17 | public: 18 | r_bool() = default; 19 | 20 | r_bool(SEXP data) { 21 | if (Rf_isLogical(data)) { 22 | if (Rf_xlength(data) == 1) { 23 | value_ = static_cast(LOGICAL_ELT(data, 0)); 24 | } 25 | } 26 | throw std::invalid_argument("Invalid r_bool value"); 27 | } 28 | 29 | r_bool(bool value) : value_(value ? TRUE : FALSE) {} 30 | r_bool(Rboolean value) : value_(value) {} 31 | r_bool(int value) : value_(from_int(value)) {} 32 | 33 | operator bool() const { return value_ == TRUE; } 34 | operator int() const { return value_; } 35 | operator Rboolean() const { return value_ ? TRUE : FALSE; } 36 | 37 | bool operator==(r_bool rhs) const { return value_ == rhs.value_; } 38 | bool operator==(bool rhs) const { return operator==(r_bool(rhs)); } 39 | bool operator==(Rboolean rhs) const { return operator==(r_bool(rhs)); } 40 | bool operator==(int rhs) const { return operator==(r_bool(rhs)); } 41 | 42 | private: 43 | static constexpr int na = std::numeric_limits::min(); 44 | 45 | static int from_int(int value) { 46 | if (value == static_cast(FALSE)) return FALSE; 47 | if (value == static_cast(na)) return na; 48 | return TRUE; 49 | } 50 | 51 | int value_ = na; 52 | }; 53 | 54 | inline std::ostream& operator<<(std::ostream& os, r_bool const& value) { 55 | os << ((value == TRUE) ? "TRUE" : "FALSE"); 56 | return os; 57 | } 58 | 59 | template 60 | using enable_if_r_bool = enable_if_t::value, R>; 61 | 62 | template 63 | enable_if_r_bool as_sexp(T from) { 64 | sexp res = Rf_allocVector(LGLSXP, 1); 65 | unwind_protect([&] { SET_LOGICAL_ELT(res.data(), 0, from); }); 66 | return res; 67 | } 68 | 69 | template <> 70 | inline r_bool na() { 71 | return NA_LOGICAL; 72 | } 73 | 74 | namespace traits { 75 | template <> 76 | struct get_underlying_type { 77 | using type = int; 78 | }; 79 | } // namespace traits 80 | 81 | } // namespace cpp11 82 | -------------------------------------------------------------------------------- /inst/include/cpp11/r_string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for string, basic_string, operator== 4 | #include // for is_convertible, enable_if 5 | 6 | #include "R_ext/Memory.h" // for vmaxget, vmaxset 7 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_mkCharCE, Rf_translat... 8 | #include "cpp11/as.hpp" // for as_sexp 9 | #include "cpp11/protect.hpp" // for unwind_protect, protect, protect::function 10 | #include "cpp11/sexp.hpp" // for sexp 11 | 12 | namespace cpp11 { 13 | 14 | class r_string { 15 | public: 16 | r_string() = default; 17 | r_string(SEXP data) : data_(data) {} 18 | r_string(const char* data) : data_(safe[Rf_mkCharCE](data, CE_UTF8)) {} 19 | r_string(const std::string& data) 20 | : data_(safe[Rf_mkCharLenCE](data.c_str(), data.size(), CE_UTF8)) {} 21 | 22 | operator SEXP() const { return data_; } 23 | operator sexp() const { return data_; } 24 | operator std::string() const { 25 | std::string res; 26 | res.reserve(size()); 27 | 28 | void* vmax = vmaxget(); 29 | unwind_protect([&] { res.assign(Rf_translateCharUTF8(data_)); }); 30 | vmaxset(vmax); 31 | 32 | return res; 33 | } 34 | 35 | bool operator==(const r_string& rhs) const { return data_.data() == rhs.data_.data(); } 36 | 37 | bool operator==(const SEXP rhs) const { return data_.data() == rhs; } 38 | 39 | bool operator==(const char* rhs) const { 40 | return static_cast(*this) == rhs; 41 | } 42 | 43 | bool operator==(const std::string& rhs) const { 44 | return static_cast(*this) == rhs; 45 | } 46 | 47 | R_xlen_t size() const { return Rf_xlength(data_); } 48 | 49 | private: 50 | sexp data_ = R_NilValue; 51 | }; 52 | 53 | inline SEXP as_sexp(std::initializer_list il) { 54 | R_xlen_t size = il.size(); 55 | 56 | sexp data; 57 | unwind_protect([&] { 58 | data = Rf_allocVector(STRSXP, size); 59 | auto it = il.begin(); 60 | for (R_xlen_t i = 0; i < size; ++i, ++it) { 61 | if (*it == NA_STRING) { 62 | SET_STRING_ELT(data, i, *it); 63 | } else { 64 | SET_STRING_ELT(data, i, Rf_mkCharCE(Rf_translateCharUTF8(*it), CE_UTF8)); 65 | } 66 | } 67 | }); 68 | return data; 69 | } 70 | 71 | template 72 | using enable_if_r_string = enable_if_t::value, R>; 73 | 74 | template 75 | enable_if_r_string as_sexp(T from) { 76 | r_string str(from); 77 | sexp res; 78 | unwind_protect([&] { 79 | res = Rf_allocVector(STRSXP, 1); 80 | 81 | if (str == NA_STRING) { 82 | SET_STRING_ELT(res, 0, str); 83 | } else { 84 | SET_STRING_ELT(res, 0, Rf_mkCharCE(Rf_translateCharUTF8(str), CE_UTF8)); 85 | } 86 | }); 87 | 88 | return res; 89 | } 90 | 91 | template <> 92 | inline r_string na() { 93 | return NA_STRING; 94 | } 95 | 96 | namespace traits { 97 | template <> 98 | struct get_underlying_type { 99 | using type = SEXP; 100 | }; 101 | } // namespace traits 102 | 103 | } // namespace cpp11 104 | -------------------------------------------------------------------------------- /inst/include/cpp11/raws.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for min 4 | #include // for array 5 | #include // for uint8_t 6 | #include // for initializer_list 7 | 8 | #include "Rversion.h" 9 | #include "cpp11/R.hpp" // for RAW, SEXP, SEXPREC, Rf_allocVector 10 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 11 | #include "cpp11/protect.hpp" // for safe 12 | #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy 13 | #include "cpp11/sexp.hpp" // for sexp 14 | 15 | // Specializations for raws 16 | 17 | namespace cpp11 { 18 | 19 | namespace traits { 20 | template <> 21 | struct get_underlying_type { 22 | using type = Rbyte; 23 | }; 24 | } // namespace traits 25 | 26 | template <> 27 | inline SEXPTYPE r_vector::get_sexptype() { 28 | return RAWSXP; 29 | } 30 | 31 | template <> 32 | inline typename r_vector::underlying_type r_vector::get_elt( 33 | SEXP x, R_xlen_t i) { 34 | // NOPROTECT: likely too costly to unwind protect every elt 35 | return RAW_ELT(x, i); 36 | } 37 | 38 | template <> 39 | inline typename r_vector::underlying_type const* r_vector::get_const_p( 40 | bool is_altrep, SEXP data) { 41 | return RAW_OR_NULL(data); 42 | } 43 | 44 | template <> 45 | inline typename r_vector::underlying_type* r_vector::get_p( 46 | bool is_altrep, SEXP data) { 47 | if (is_altrep) { 48 | return nullptr; 49 | } else { 50 | return RAW(data); 51 | } 52 | } 53 | 54 | template <> 55 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 56 | typename r_vector::underlying_type* buf) { 57 | // NOPROTECT: likely too costly to unwind protect here 58 | RAW_GET_REGION(x, i, n, buf); 59 | } 60 | 61 | template <> 62 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 63 | return is_altrep; 64 | } 65 | 66 | typedef r_vector raws; 67 | 68 | namespace writable { 69 | 70 | template <> 71 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 72 | typename r_vector::underlying_type value) { 73 | // NOPROTECT: Likely too costly to unwind protect every set elt 74 | #if R_VERSION >= R_Version(4, 2, 0) 75 | SET_RAW_ELT(x, i, value); 76 | #else 77 | RAW(x)[i] = value; 78 | #endif 79 | } 80 | 81 | typedef r_vector raws; 82 | 83 | } // namespace writable 84 | 85 | } // namespace cpp11 86 | -------------------------------------------------------------------------------- /inst/include/cpp11/sexp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for size_t 4 | 5 | #include // for string, basic_string 6 | 7 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, REAL_ELT, R_NilV... 8 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 9 | #include "cpp11/protect.hpp" // for store 10 | 11 | namespace cpp11 { 12 | 13 | /// Converting to SEXP 14 | class sexp { 15 | private: 16 | SEXP data_ = R_NilValue; 17 | SEXP preserve_token_ = R_NilValue; 18 | 19 | public: 20 | sexp() = default; 21 | 22 | sexp(SEXP data) : data_(data), preserve_token_(detail::store::insert(data_)) {} 23 | 24 | // We maintain our own new `preserve_token_` 25 | sexp(const sexp& rhs) { 26 | data_ = rhs.data_; 27 | preserve_token_ = detail::store::insert(data_); 28 | } 29 | 30 | // We take ownership over the `rhs.preserve_token_`. 31 | // Importantly we clear it in the `rhs` so it can't release the object upon destruction. 32 | sexp(sexp&& rhs) { 33 | data_ = rhs.data_; 34 | preserve_token_ = rhs.preserve_token_; 35 | 36 | rhs.data_ = R_NilValue; 37 | rhs.preserve_token_ = R_NilValue; 38 | } 39 | 40 | sexp& operator=(const sexp& rhs) { 41 | detail::store::release(preserve_token_); 42 | 43 | data_ = rhs.data_; 44 | preserve_token_ = detail::store::insert(data_); 45 | 46 | return *this; 47 | } 48 | 49 | ~sexp() { detail::store::release(preserve_token_); } 50 | 51 | attribute_proxy attr(const char* name) const { 52 | return attribute_proxy(*this, name); 53 | } 54 | 55 | attribute_proxy attr(const std::string& name) const { 56 | return attribute_proxy(*this, name.c_str()); 57 | } 58 | 59 | attribute_proxy attr(SEXP name) const { 60 | return attribute_proxy(*this, name); 61 | } 62 | 63 | attribute_proxy names() const { 64 | return attribute_proxy(*this, R_NamesSymbol); 65 | } 66 | 67 | operator SEXP() const { return data_; } 68 | SEXP data() const { return data_; } 69 | 70 | /// DEPRECATED: Do not use this, it will be removed soon. 71 | operator double() const { return REAL_ELT(data_, 0); } 72 | /// DEPRECATED: Do not use this, it will be removed soon. 73 | operator size_t() const { return REAL_ELT(data_, 0); } 74 | /// DEPRECATED: Do not use this, it will be removed soon. 75 | operator bool() const { return LOGICAL_ELT(data_, 0); } 76 | }; 77 | 78 | } // namespace cpp11 79 | -------------------------------------------------------------------------------- /inst/include/cpp11/strings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for initializer_list 4 | #include // for string, basic_string 5 | 6 | #include "cpp11/R.hpp" // for SEXP, SEXPREC, SET_STRI... 7 | #include "cpp11/as.hpp" // for as_sexp 8 | #include "cpp11/attribute_proxy.hpp" // for attribute_proxy 9 | #include "cpp11/named_arg.hpp" // for named_arg 10 | #include "cpp11/protect.hpp" // for safe 11 | #include "cpp11/r_string.hpp" // for r_string 12 | #include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy 13 | #include "cpp11/sexp.hpp" // for sexp 14 | 15 | // Specializations for strings 16 | 17 | namespace cpp11 { 18 | 19 | template <> 20 | inline SEXPTYPE r_vector::get_sexptype() { 21 | return STRSXP; 22 | } 23 | 24 | template <> 25 | inline typename r_vector::underlying_type r_vector::get_elt( 26 | SEXP x, R_xlen_t i) { 27 | // NOPROTECT: likely too costly to unwind protect every elt 28 | return STRING_ELT(x, i); 29 | } 30 | 31 | template <> 32 | inline typename r_vector::underlying_type* r_vector::get_p(bool, 33 | SEXP) { 34 | return nullptr; 35 | } 36 | 37 | template <> 38 | inline typename r_vector::underlying_type const* 39 | r_vector::get_const_p(bool is_altrep, SEXP data) { 40 | // No `STRING_PTR_OR_NULL()` 41 | if (is_altrep) { 42 | return nullptr; 43 | } else { 44 | return STRING_PTR_RO(data); 45 | } 46 | } 47 | 48 | template <> 49 | inline void r_vector::get_region(SEXP x, R_xlen_t i, R_xlen_t n, 50 | typename r_vector::underlying_type* buf) { 51 | cpp11::stop("Unreachable!"); 52 | } 53 | 54 | template <> 55 | inline bool r_vector::const_iterator::use_buf(bool is_altrep) { 56 | return false; 57 | } 58 | 59 | typedef r_vector strings; 60 | 61 | namespace writable { 62 | 63 | template <> 64 | inline void r_vector::set_elt(SEXP x, R_xlen_t i, 65 | typename r_vector::underlying_type value) { 66 | // NOPROTECT: Likely too costly to unwind protect every set elt 67 | SET_STRING_ELT(x, i, value); 68 | } 69 | 70 | inline bool operator==(const r_vector::proxy& lhs, r_string rhs) { 71 | return static_cast(lhs).operator==(static_cast(rhs).c_str()); 72 | } 73 | 74 | inline SEXP alloc_or_copy(const SEXP data) { 75 | switch (detail::r_typeof(data)) { 76 | case CHARSXP: 77 | return cpp11::r_vector(safe[Rf_allocVector](STRSXP, 1)); 78 | case STRSXP: 79 | return safe[Rf_shallow_duplicate](data); 80 | default: 81 | throw type_error(STRSXP, detail::r_typeof(data)); 82 | } 83 | } 84 | 85 | inline SEXP alloc_if_charsxp(const SEXP data) { 86 | switch (detail::r_typeof(data)) { 87 | case CHARSXP: 88 | return cpp11::r_vector(safe[Rf_allocVector](STRSXP, 1)); 89 | case STRSXP: 90 | return data; 91 | default: 92 | throw type_error(STRSXP, detail::r_typeof(data)); 93 | } 94 | } 95 | 96 | template <> 97 | inline r_vector::r_vector(const SEXP& data) 98 | : cpp11::r_vector(alloc_or_copy(data)), capacity_(length_) { 99 | if (detail::r_typeof(data) == CHARSXP) { 100 | SET_STRING_ELT(data_, 0, data); 101 | } 102 | } 103 | 104 | template <> 105 | inline r_vector::r_vector(SEXP&& data) 106 | : cpp11::r_vector(alloc_if_charsxp(data)), capacity_(length_) { 107 | if (detail::r_typeof(data) == CHARSXP) { 108 | SET_STRING_ELT(data_, 0, data); 109 | } 110 | } 111 | 112 | // Requires specialization to handle `NA_STRING` and UTF-8 translation 113 | template <> 114 | inline r_vector::r_vector(std::initializer_list il) 115 | : cpp11::r_vector(safe[Rf_allocVector](STRSXP, il.size())), 116 | capacity_(il.size()) { 117 | unwind_protect([&] { 118 | auto it = il.begin(); 119 | 120 | for (R_xlen_t i = 0; i < capacity_; ++i, ++it) { 121 | // i.e. to `SEXP` 122 | underlying_type elt = static_cast(*it); 123 | 124 | if (elt == NA_STRING) { 125 | set_elt(data_, i, elt); 126 | } else { 127 | set_elt(data_, i, Rf_mkCharCE(Rf_translateCharUTF8(elt), CE_UTF8)); 128 | } 129 | } 130 | }); 131 | } 132 | 133 | typedef r_vector strings; 134 | 135 | template 136 | inline void r_vector::push_back(const named_arg& value) { 137 | push_back(value.value()); 138 | if (Rf_xlength(names()) == 0) { 139 | cpp11::writable::strings new_nms(size()); 140 | names() = new_nms; 141 | } 142 | cpp11::writable::strings nms(names()); 143 | nms[size() - 1] = value.name(); 144 | } 145 | 146 | } // namespace writable 147 | 148 | } // namespace cpp11 149 | -------------------------------------------------------------------------------- /man/cpp11-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cpp11-package.R 3 | \docType{package} 4 | \name{cpp11-package} 5 | \alias{cpp11} 6 | \alias{cpp11-package} 7 | \title{cpp11: A C++11 Interface for R's C Interface} 8 | \description{ 9 | Provides a header only, C++11 interface to R's C interface. Compared to other approaches 'cpp11' strives to be safe against long jumps from the C API as well as C++ exceptions, conform to normal R function semantics and supports interaction with 'ALTREP' vectors. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://cpp11.r-lib.org} 15 | \item \url{https://github.com/r-lib/cpp11} 16 | \item Report bugs at \url{https://github.com/r-lib/cpp11/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Davis Vaughan \email{davis@posit.co} (\href{https://orcid.org/0000-0003-4777-038X}{ORCID}) 22 | 23 | Authors: 24 | \itemize{ 25 | \item Jim Hester (\href{https://orcid.org/0000-0002-2739-7082}{ORCID}) 26 | \item Romain François (\href{https://orcid.org/0000-0002-2444-4226}{ORCID}) 27 | } 28 | 29 | Other contributors: 30 | \itemize{ 31 | \item Benjamin Kietzman [contributor] 32 | \item Posit Software, PBC [copyright holder, funder] 33 | } 34 | 35 | } 36 | \keyword{internal} 37 | -------------------------------------------------------------------------------- /man/cpp_register.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/register.R 3 | \name{cpp_register} 4 | \alias{cpp_register} 5 | \title{Generates wrappers for registered C++ functions} 6 | \usage{ 7 | cpp_register( 8 | path = ".", 9 | quiet = !is_interactive(), 10 | extension = c(".cpp", ".cc") 11 | ) 12 | } 13 | \arguments{ 14 | \item{path}{The path to the package root directory} 15 | 16 | \item{quiet}{If \code{TRUE} suppresses output from this function} 17 | 18 | \item{extension}{The file extension to use for the generated src/cpp11 file. 19 | \code{.cpp} by default, but \code{.cc} is also supported.} 20 | } 21 | \value{ 22 | The paths to the generated R and C++ source files (in that order). 23 | } 24 | \description{ 25 | Functions decorated with \verb{[[cpp11::register]]} in files ending in \code{.cc}, 26 | \code{.cpp}, \code{.h} or \code{.hpp} will be wrapped in generated code and registered to 27 | be called from R. 28 | } 29 | \details{ 30 | Note registered functions will not be \emph{exported} from your package unless 31 | you also add a \verb{@export} roxygen2 directive for them. 32 | 33 | In order to use \code{cpp_register()} the \code{cli}, \code{decor}, \code{desc}, \code{glue}, 34 | \code{tibble} and \code{vctrs} packages must also be installed. 35 | } 36 | \examples{ 37 | # create a minimal package 38 | dir <- tempfile() 39 | dir.create(dir) 40 | 41 | writeLines("Package: testPkg", file.path(dir, "DESCRIPTION")) 42 | writeLines("useDynLib(testPkg, .registration = TRUE)", file.path(dir, "NAMESPACE")) 43 | 44 | # create a C++ file with a decorated function 45 | dir.create(file.path(dir, "src")) 46 | writeLines("[[cpp11::register]] int one() { return 1; }", file.path(dir, "src", "one.cpp")) 47 | 48 | # register the functions in the package 49 | cpp_register(dir) 50 | 51 | # Files generated by registration 52 | file.exists(file.path(dir, "R", "cpp11.R")) 53 | file.exists(file.path(dir, "src", "cpp11.cpp")) 54 | 55 | # cleanup 56 | unlink(dir, recursive = TRUE) 57 | } 58 | -------------------------------------------------------------------------------- /man/cpp_source.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/source.R 3 | \name{cpp_source} 4 | \alias{cpp_source} 5 | \alias{cpp_function} 6 | \alias{cpp_eval} 7 | \title{Compile C++ code} 8 | \usage{ 9 | cpp_source( 10 | file, 11 | code = NULL, 12 | env = parent.frame(), 13 | clean = TRUE, 14 | quiet = TRUE, 15 | cxx_std = Sys.getenv("CXX_STD", "CXX11"), 16 | dir = tempfile() 17 | ) 18 | 19 | cpp_function( 20 | code, 21 | env = parent.frame(), 22 | clean = TRUE, 23 | quiet = TRUE, 24 | cxx_std = Sys.getenv("CXX_STD", "CXX11") 25 | ) 26 | 27 | cpp_eval( 28 | code, 29 | env = parent.frame(), 30 | clean = TRUE, 31 | quiet = TRUE, 32 | cxx_std = Sys.getenv("CXX_STD", "CXX11") 33 | ) 34 | } 35 | \arguments{ 36 | \item{file}{A file containing C++ code to compile} 37 | 38 | \item{code}{If non-null, the C++ code to compile} 39 | 40 | \item{env}{The R environment where the R wrapping functions should be defined.} 41 | 42 | \item{clean}{If \code{TRUE}, cleanup the files after sourcing} 43 | 44 | \item{quiet}{If 'TRUE`, do not show compiler output} 45 | 46 | \item{cxx_std}{The C++ standard to use, the \code{CXX_STD} make macro is set to 47 | this value. The default value queries the \code{CXX_STD} environment variable, or 48 | uses 'CXX11' if unset.} 49 | 50 | \item{dir}{The directory to store the generated source files. \code{tempfile()} is 51 | used by default. The directory will be removed if \code{clean} is \code{TRUE}.} 52 | } 53 | \value{ 54 | For \code{\link[=cpp_source]{cpp_source()}} and \verb{[cpp_function()]} the results of 55 | \code{\link[=dyn.load]{dyn.load()}} (invisibly). For \verb{[cpp_eval()]} the results of the evaluated 56 | expression. 57 | } 58 | \description{ 59 | \code{\link[=cpp_source]{cpp_source()}} compiles and loads a single C++ file for use in R. 60 | \code{\link[=cpp_function]{cpp_function()}} compiles and loads a single function for use in R. 61 | \code{\link[=cpp_eval]{cpp_eval()}} evaluates a single C++ expression and returns the result. 62 | } 63 | \details{ 64 | Within C++ code you can use \verb{[[cpp11::linking_to("pkgxyz")]]} to link to 65 | external packages. This is equivalent to putting those packages in the 66 | \code{LinkingTo} field in a package DESCRIPTION. 67 | } 68 | \examples{ 69 | 70 | cpp_source( 71 | code = '#include "cpp11/integers.hpp" 72 | 73 | [[cpp11::register]] 74 | int num_odd(cpp11::integers x) { 75 | int total = 0; 76 | for (int val : x) { 77 | if ((val \% 2) == 1) { 78 | ++total; 79 | } 80 | } 81 | return total; 82 | } 83 | ') 84 | 85 | num_odd(as.integer(c(1:10, 15, 23))) 86 | 87 | if (interactive() && require("progress")) { 88 | 89 | cpp_source( 90 | code = ' 91 | #include 92 | #include 93 | 94 | [[cpp11::linking_to("progress")]] 95 | 96 | [[cpp11::register]] void 97 | show_progress() { 98 | RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 99 | 100 | pb.tick(0); 101 | for (int i = 0; i < 100; i++) { 102 | usleep(2.0 / 100 * 1000000); 103 | pb.tick(); 104 | } 105 | } 106 | ') 107 | 108 | show_progress() 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /man/cpp_vendor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vendor.R 3 | \name{cpp_vendor} 4 | \alias{cpp_vendor} 5 | \title{Vendor the cpp11 dependency} 6 | \usage{ 7 | cpp_vendor(path = ".") 8 | } 9 | \arguments{ 10 | \item{path}{The path to the package root directory} 11 | } 12 | \value{ 13 | The file path to the vendored code (invisibly). 14 | } 15 | \description{ 16 | Vendoring is the act of making your own copy of the 3rd party packages your 17 | project is using. It is often used in the go language community. 18 | } 19 | \details{ 20 | This function vendors cpp11 into your package by copying the cpp11 21 | headers into the \code{inst/include} folder of your package and adding 22 | 'cpp11 version: XYZ' to the top of the files, where XYZ is the version of 23 | cpp11 currently installed on your machine. 24 | 25 | If you choose to vendor the headers you should \emph{remove} \code{LinkingTo: cpp11} from your DESCRIPTION. 26 | 27 | \strong{Note}: vendoring places the responsibility of updating the code on 28 | \strong{you}. Bugfixes and new features in cpp11 will not be available for your 29 | code until you run \code{cpp_vendor()} again. 30 | } 31 | \examples{ 32 | # create a new directory 33 | dir <- tempfile() 34 | dir.create(dir) 35 | 36 | # vendor the cpp11 headers into the directory 37 | cpp_vendor(dir) 38 | 39 | list.files(file.path(dir, "inst", "include", "cpp11")) 40 | 41 | # cleanup 42 | unlink(dir, recursive = TRUE) 43 | } 44 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | data.sqlite 6 | *.html 7 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | ## Failed to check (10) 4 | 5 | |package |version |error |warning |note | 6 | |:---------------|:-------|:-----|:-------|:----| 7 | |BiocParallel |? | | | | 8 | |CytoML |? | | | | 9 | |flowCore |? | | | | 10 | |flowWorkspace |? | | | | 11 | |mnmer |? | | | | 12 | |NanoMethViz |? | | | | 13 | |ncdfFlow |? | | | | 14 | |openCyto |? | | | | 15 | |pkgstats |? | | | | 16 | |rankUncertainty |? | | | | 17 | 18 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 85 reverse dependencies (75 from CRAN + 10 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /revdep/email.yml: -------------------------------------------------------------------------------- 1 | release_date: ??? 2 | rel_release_date: ??? 3 | my_news_url: ??? 4 | release_version: ??? 5 | release_details: ??? 6 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | # BiocParallel 2 | 3 |
4 | 5 | * Version: NA 6 | * GitHub: NA 7 | * Source code: https://github.com/cran/BiocParallel 8 | * Number of recursive dependencies: 116 9 | 10 | Run `revdepcheck::cloud_details(, "BiocParallel")` for more info 11 | 12 |
13 | 14 | ## Error before installation 15 | 16 | ### Devel 17 | 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | 25 | ``` 26 | ### CRAN 27 | 28 | ``` 29 | 30 | 31 | 32 | 33 | 34 | 35 | ``` 36 | # CytoML 37 | 38 |
39 | 40 | * Version: NA 41 | * GitHub: NA 42 | * Source code: https://github.com/cran/CytoML 43 | * Number of recursive dependencies: 108 44 | 45 | Run `revdepcheck::cloud_details(, "CytoML")` for more info 46 | 47 |
48 | 49 | ## Error before installation 50 | 51 | ### Devel 52 | 53 | ``` 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | ### CRAN 62 | 63 | ``` 64 | 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | # flowCore 72 | 73 |
74 | 75 | * Version: NA 76 | * GitHub: NA 77 | * Source code: https://github.com/cran/flowCore 78 | * Number of recursive dependencies: 119 79 | 80 | Run `revdepcheck::cloud_details(, "flowCore")` for more info 81 | 82 |
83 | 84 | ## Error before installation 85 | 86 | ### Devel 87 | 88 | ``` 89 | 90 | 91 | 92 | 93 | 94 | 95 | ``` 96 | ### CRAN 97 | 98 | ``` 99 | 100 | 101 | 102 | 103 | 104 | 105 | ``` 106 | # flowWorkspace 107 | 108 |
109 | 110 | * Version: NA 111 | * GitHub: NA 112 | * Source code: https://github.com/cran/flowWorkspace 113 | * Number of recursive dependencies: 108 114 | 115 | Run `revdepcheck::cloud_details(, "flowWorkspace")` for more info 116 | 117 |
118 | 119 | ## Error before installation 120 | 121 | ### Devel 122 | 123 | ``` 124 | 125 | 126 | 127 | 128 | 129 | 130 | ``` 131 | ### CRAN 132 | 133 | ``` 134 | 135 | 136 | 137 | 138 | 139 | 140 | ``` 141 | # mnmer 142 | 143 |
144 | 145 | * Version: NA 146 | * GitHub: NA 147 | * Source code: https://github.com/cran/mnmer 148 | * Number of recursive dependencies: 131 149 | 150 | Run `revdepcheck::cloud_details(, "mnmer")` for more info 151 | 152 |
153 | 154 | ## Error before installation 155 | 156 | ### Devel 157 | 158 | ``` 159 | 160 | 161 | 162 | 163 | 164 | 165 | ``` 166 | ### CRAN 167 | 168 | ``` 169 | 170 | 171 | 172 | 173 | 174 | 175 | ``` 176 | # NanoMethViz 177 | 178 |
179 | 180 | * Version: NA 181 | * GitHub: NA 182 | * Source code: https://github.com/cran/NanoMethViz 183 | * Number of recursive dependencies: 194 184 | 185 | Run `revdepcheck::cloud_details(, "NanoMethViz")` for more info 186 | 187 |
188 | 189 | ## Error before installation 190 | 191 | ### Devel 192 | 193 | ``` 194 | 195 | 196 | 197 | 198 | 199 | 200 | ``` 201 | ### CRAN 202 | 203 | ``` 204 | 205 | 206 | 207 | 208 | 209 | 210 | ``` 211 | # ncdfFlow 212 | 213 |
214 | 215 | * Version: NA 216 | * GitHub: NA 217 | * Source code: https://github.com/cran/ncdfFlow 218 | * Number of recursive dependencies: 113 219 | 220 | Run `revdepcheck::cloud_details(, "ncdfFlow")` for more info 221 | 222 |
223 | 224 | ## Error before installation 225 | 226 | ### Devel 227 | 228 | ``` 229 | 230 | 231 | 232 | 233 | 234 | 235 | ``` 236 | ### CRAN 237 | 238 | ``` 239 | 240 | 241 | 242 | 243 | 244 | 245 | ``` 246 | # openCyto 247 | 248 |
249 | 250 | * Version: NA 251 | * GitHub: NA 252 | * Source code: https://github.com/cran/openCyto 253 | * Number of recursive dependencies: 135 254 | 255 | Run `revdepcheck::cloud_details(, "openCyto")` for more info 256 | 257 |
258 | 259 | ## Error before installation 260 | 261 | ### Devel 262 | 263 | ``` 264 | 265 | 266 | 267 | 268 | 269 | 270 | ``` 271 | ### CRAN 272 | 273 | ``` 274 | 275 | 276 | 277 | 278 | 279 | 280 | ``` 281 | # pkgstats 282 | 283 |
284 | 285 | * Version: NA 286 | * GitHub: NA 287 | * Source code: https://github.com/cran/pkgstats 288 | * Number of recursive dependencies: 78 289 | 290 | Run `revdepcheck::cloud_details(, "pkgstats")` for more info 291 | 292 |
293 | 294 | ## Error before installation 295 | 296 | ### Devel 297 | 298 | ``` 299 | 300 | 301 | 302 | 303 | 304 | 305 | ``` 306 | ### CRAN 307 | 308 | ``` 309 | 310 | 311 | 312 | 313 | 314 | 315 | ``` 316 | # rankUncertainty 317 | 318 |
319 | 320 | * Version: NA 321 | * GitHub: NA 322 | * Source code: https://github.com/cran/rankUncertainty 323 | * Number of recursive dependencies: 48 324 | 325 | Run `revdepcheck::cloud_details(, "rankUncertainty")` for more info 326 | 327 |
328 | 329 | ## Error before installation 330 | 331 | ### Devel 332 | 333 | ``` 334 | 335 | 336 | 337 | 338 | 339 | 340 | ``` 341 | ### CRAN 342 | 343 | ``` 344 | 345 | 346 | 347 | 348 | 349 | 350 | ``` 351 | -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(cpp11) 3 | 4 | test_check("cpp11") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/register.md: -------------------------------------------------------------------------------- 1 | # get_call_entries: returns an empty string for packages with .Call entries and NAMESPACE files 2 | 3 | Code 4 | call_entries 5 | Output 6 | [1] "/* .Call calls */" 7 | [2] "extern SEXP bar(void);" 8 | [3] "" 9 | [4] "static const R_CallMethodDef CallEntries[] = {" 10 | [5] " {\"bar\", (DL_FUNC) &bar, 0}," 11 | [6] " {NULL, NULL, 0}" 12 | [7] "};" 13 | 14 | # get_call_entries: works with multiple register functions. 15 | 16 | Code 17 | cat(read_file(cpp_bindings)) 18 | Output 19 | // Generated by cpp11: do not edit by hand 20 | // clang-format off 21 | 22 | 23 | #include "cpp11/declarations.hpp" 24 | #include 25 | 26 | // multiple.cpp 27 | int foo(); 28 | extern "C" SEXP _testPkg_foo() { 29 | BEGIN_CPP11 30 | return cpp11::as_sexp(foo()); 31 | END_CPP11 32 | } 33 | // multiple.cpp 34 | double bar(bool run); 35 | extern "C" SEXP _testPkg_bar(SEXP run) { 36 | BEGIN_CPP11 37 | return cpp11::as_sexp(bar(cpp11::as_cpp>(run))); 38 | END_CPP11 39 | } 40 | // multiple.cpp 41 | bool baz(bool run, int value); 42 | extern "C" SEXP _testPkg_baz(SEXP run, SEXP value) { 43 | BEGIN_CPP11 44 | return cpp11::as_sexp(baz(cpp11::as_cpp>(run), cpp11::as_cpp>(value))); 45 | END_CPP11 46 | } 47 | 48 | extern "C" { 49 | static const R_CallMethodDef CallEntries[] = { 50 | {"_testPkg_bar", (DL_FUNC) &_testPkg_bar, 1}, 51 | {"_testPkg_baz", (DL_FUNC) &_testPkg_baz, 2}, 52 | {"_testPkg_foo", (DL_FUNC) &_testPkg_foo, 0}, 53 | {NULL, NULL, 0} 54 | }; 55 | } 56 | 57 | extern "C" attribute_visible void R_init_testPkg(DllInfo* dll){ 58 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 59 | R_useDynamicSymbols(dll, FALSE); 60 | R_forceSymbols(dll, TRUE); 61 | } 62 | 63 | # cpp_register: works with a package that registers a single c++ function 64 | 65 | Code 66 | cat(read_file(r_bindings)) 67 | Output 68 | # Generated by cpp11: do not edit by hand 69 | 70 | foo <- function() { 71 | .Call(`_testPkg_foo`) 72 | } 73 | 74 | --- 75 | 76 | Code 77 | cat(read_file(cpp_bindings)) 78 | Output 79 | // Generated by cpp11: do not edit by hand 80 | // clang-format off 81 | 82 | 83 | #include "cpp11/declarations.hpp" 84 | #include 85 | 86 | // single.cpp 87 | int foo(); 88 | extern "C" SEXP _testPkg_foo() { 89 | BEGIN_CPP11 90 | return cpp11::as_sexp(foo()); 91 | END_CPP11 92 | } 93 | 94 | extern "C" { 95 | static const R_CallMethodDef CallEntries[] = { 96 | {"_testPkg_foo", (DL_FUNC) &_testPkg_foo, 0}, 97 | {NULL, NULL, 0} 98 | }; 99 | } 100 | 101 | extern "C" attribute_visible void R_init_testPkg(DllInfo* dll){ 102 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 103 | R_useDynamicSymbols(dll, FALSE); 104 | R_forceSymbols(dll, TRUE); 105 | } 106 | 107 | # cpp_register: can be run with messages 108 | 109 | Code 110 | cpp_register(p, quiet = FALSE) 111 | Message 112 | i 1 functions decorated with [[cpp11::register]] 113 | v generated file 'cpp11.R' 114 | v generated file 'cpp11.cpp' 115 | 116 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/source.md: -------------------------------------------------------------------------------- 1 | # cpp_source fails informatively for nonexistent file 2 | 3 | Code 4 | cpp_source(i_do_not_exist) 5 | Condition 6 | Error: 7 | ! Can't find `file` at this path: 8 | {NON_EXISTENT_FILEPATH} 9 | 10 | -------------------------------------------------------------------------------- /tests/testthat/helper.R: -------------------------------------------------------------------------------- 1 | local_package <- function() { 2 | dir <- tempfile() 3 | dir.create(dir) 4 | withr::defer(unlink(dir, recursive = TRUE), parent.frame()) 5 | 6 | writeLines("Package: testPkg", file.path(dir, "DESCRIPTION")) 7 | writeLines("useDynLib(testPkg, .registration = TRUE)", file.path(dir, "NAMESPACE")) 8 | desc::desc(dir) 9 | } 10 | 11 | pkg_path <- function(pkg) { 12 | dirname(pkg$.__enclos_env__$private$path) 13 | } 14 | 15 | get_funs <- function(path) { 16 | all_decorations <- decor::cpp_decorations(path, is_attribute = TRUE) 17 | get_registered_functions(all_decorations, "cpp11::register", quiet = TRUE) 18 | } 19 | 20 | get_package_name <- function(path) { 21 | desc::desc_get("Package", file = file.path(path, "DESCRIPTION")) 22 | } 23 | 24 | glue_str <- function(...) { 25 | glue::as_glue(unlist(list(...))) 26 | } 27 | 28 | read_file <- function(x) { 29 | readChar(x, file.size(x)) 30 | } 31 | 32 | expect_error_free <- function(..., regexp = NA) { 33 | expect_error(..., regexp = regexp) 34 | } 35 | -------------------------------------------------------------------------------- /tests/testthat/linking_to_incorrect_registers.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::link_to("progress")]] 2 | 3 | [[cpp11::register]] void 4 | show_progress() { 5 | RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 6 | 7 | pb.tick(0); 8 | for (int i = 0; i < 100; i++) { 9 | usleep(2.0 / 100 * 1000000); 10 | pb.tick(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/testthat/linking_to_registers.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::linking_to("progress")]] 2 | 3 | [[cpp11::register]] void 4 | show_progress() { 5 | RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 6 | 7 | pb.tick(0); 8 | for (int i = 0; i < 100; i++) { 9 | usleep(2.0 / 100 * 1000000); 10 | pb.tick(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/testthat/multiple.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::register]] int foo() { return 1; } 2 | 3 | [[cpp11::register]] double bar(bool run) { return 1.0; } 4 | 5 | [[cpp11::register]] bool baz(bool run, int value = 0) { return true; } 6 | -------------------------------------------------------------------------------- /tests/testthat/multiple_incorrect.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::registe]] int foo() { return 1; } 2 | 3 | [[cpp11::register]] double bar(bool run) { return 1.0; } 4 | 5 | [[cpp11::reg]] bool baz(bool run, int value = 0) { return true; } 6 | -------------------------------------------------------------------------------- /tests/testthat/single.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::register]] int foo() { return 1; } 2 | -------------------------------------------------------------------------------- /tests/testthat/single_error.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::register]] int foo() { return 1 } 2 | -------------------------------------------------------------------------------- /tests/testthat/single_incorrect.cpp: -------------------------------------------------------------------------------- 1 | [[cpp11::registe]] int foo() { return 1; } 2 | -------------------------------------------------------------------------------- /tests/testthat/test-knitr.R: -------------------------------------------------------------------------------- 1 | describe("eng_cpp11", { 2 | it("works when code is not evaluated", { 3 | skip_on_os("solaris") 4 | opts <- knitr::opts_chunk$get() 5 | opts <- utils::modifyList(opts, list(eval = FALSE, engine = "cpp11", code = "1 + 1")) 6 | 7 | expect_equal( 8 | eng_cpp11(opts), 9 | "1 + 1" 10 | ) 11 | }) 12 | 13 | it("works when code is evaluated", { 14 | skip_on_os("solaris") 15 | opts <- knitr::opts_chunk$get() 16 | code <- "[[cpp11::register]] int foo() { return 0; }" 17 | opts <- utils::modifyList(opts, list(eval = TRUE, engine = "cpp11", code = code, quiet = TRUE)) 18 | 19 | expect_equal( 20 | eng_cpp11(opts), 21 | code 22 | ) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/testthat/test-source.R: -------------------------------------------------------------------------------- 1 | test_that("cpp_source works with the `code` parameter", { 2 | skip_on_os("solaris") 3 | dll_info <- cpp_source( 4 | code = ' 5 | #include "cpp11/integers.hpp" 6 | 7 | [[cpp11::register]] 8 | int num_odd(cpp11::integers x) { 9 | int total = 0; 10 | for (int val : x) { 11 | if ((val % 2) == 1) { 12 | ++total; 13 | } 14 | } 15 | return total; 16 | } 17 | ', clean = TRUE) 18 | on.exit(dyn.unload(dll_info[["path"]])) 19 | 20 | expect_equal(num_odd(as.integer(c(1:10, 15, 23))), 7) 21 | }) 22 | 23 | test_that("cpp_source works with the `file` parameter", { 24 | skip_on_os("solaris") 25 | tf <- tempfile(fileext = ".cpp") 26 | writeLines( 27 | "[[cpp11::register]] 28 | bool always_true() { 29 | return true; 30 | } 31 | ", tf) 32 | on.exit(unlink(tf)) 33 | 34 | dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE) 35 | on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 36 | 37 | expect_true(always_true()) 38 | }) 39 | 40 | test_that("cpp_source works with files called `cpp11.cpp`", { 41 | skip_on_os("solaris") 42 | tf <- file.path(tempdir(), "cpp11.cpp") 43 | writeLines( 44 | "[[cpp11::register]] 45 | bool always_true() { 46 | return true; 47 | } 48 | ", tf) 49 | on.exit(unlink(tf)) 50 | 51 | dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE) 52 | on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 53 | 54 | expect_true(always_true()) 55 | }) 56 | 57 | test_that("cpp_source returns original file name on error", { 58 | 59 | expect_output(try(cpp_source(test_path("single_error.cpp"), clean = TRUE), silent = TRUE), 60 | normalizePath(test_path("single_error.cpp"), winslash = "/"), fixed = TRUE) 61 | 62 | #error generated for incorrect attributes is separate from compilation errors 63 | expect_error(cpp_source(test_path("single_incorrect.cpp"), clean = TRUE), 64 | normalizePath(test_path("single_incorrect.cpp"), winslash = "/"), fixed = TRUE) 65 | 66 | }) 67 | 68 | test_that("cpp_source lets you set the C++ standard", { 69 | skip_on_os("solaris") 70 | skip_on_os("windows") # Older windows toolchains do not support C++14 71 | tf <- tempfile(fileext = ".cpp") 72 | writeLines( 73 | '#include 74 | using namespace std::string_literals; 75 | [[cpp11::register]] 76 | std::string fun() { 77 | auto str = "hello_world"s; 78 | return str; 79 | } 80 | ', tf) 81 | on.exit(unlink(tf)) 82 | 83 | dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE, cxx_std = "CXX14") 84 | on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 85 | 86 | expect_equal(fun(), "hello_world") 87 | }) 88 | 89 | test_that("generate_cpp_name works", { 90 | expect_equal( 91 | generate_cpp_name("foo.cpp"), 92 | "foo.cpp" 93 | ) 94 | 95 | expect_equal( 96 | generate_cpp_name("foo.cpp", loaded_dlls = "foo"), 97 | "foo_2.cpp" 98 | ) 99 | 100 | expect_equal( 101 | generate_cpp_name("foo.cpp", loaded_dlls = c("foo", "foo_2")), 102 | "foo_3.cpp" 103 | ) 104 | }) 105 | 106 | test_that("generate_include_paths handles paths with spaces", { 107 | if (is_windows()) { 108 | mockery::stub(generate_include_paths, "system.file", "C:\\a path with spaces\\cpp11") 109 | expect_equal(generate_include_paths("cpp11"), "-I\"C:\\a path with spaces\\cpp11\"") 110 | } else { 111 | mockery::stub(generate_include_paths, "system.file", "/a path with spaces/cpp11") 112 | expect_equal(generate_include_paths("cpp11"), "-I'/a path with spaces/cpp11'") 113 | } 114 | }) 115 | 116 | test_that("check_valid_attributes does not return an error if all registers are correct", { 117 | expect_error_free( 118 | cpp11::cpp_source(clean = TRUE, code = '#include 119 | using namespace cpp11::literals; 120 | [[cpp11::register]] 121 | cpp11::list fn() { 122 | cpp11::writable::list x; 123 | x.push_back({"foo"_nm = 1}); 124 | return x; 125 | } 126 | [[cpp11::register]] 127 | cpp11::list fn2() { 128 | cpp11::writable::list x; 129 | x.push_back({"foo"_nm = 1}); 130 | return x; 131 | }')) 132 | expect_error_free( 133 | cpp11::cpp_source(clean = TRUE, 134 | code = '#include 135 | #include 136 | 137 | [[cpp11::linking_to("progress")]] 138 | 139 | [[cpp11::register]] void show_progress() { 140 | RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 141 | 142 | pb.tick(0); 143 | for (int i = 0; i < 100; i++) { 144 | usleep(2.0 / 100 * 1000000); 145 | pb.tick(); 146 | } 147 | } 148 | ') 149 | ) 150 | }) 151 | 152 | test_that("check_valid_attributes returns an error if one or more registers is incorrect", { 153 | expect_error( 154 | cpp11::cpp_source(code = '#include 155 | using namespace cpp11::literals; 156 | [[cpp11::reg]] 157 | cpp11::list fn() { 158 | cpp11::writable::list x; 159 | x.push_back({"foo"_nm = 1}); 160 | return x; 161 | } 162 | [[cpp11::register]] 163 | cpp11::list fn2() { 164 | cpp11::writable::list x; 165 | x.push_back({"foo"_nm = 1}); 166 | return x; 167 | }')) 168 | 169 | expect_error( 170 | cpp11::cpp_source(code = '#include 171 | using namespace cpp11::literals; 172 | [[cpp11::reg]] 173 | cpp11::list fn() { 174 | cpp11::writable::list x; 175 | x.push_back({"foo"_nm = 1}); 176 | return x; 177 | }')) 178 | 179 | expect_error( 180 | cpp11::cpp_source(code = '#include 181 | using namespace cpp11::literals; 182 | [[cpp11::reg]] 183 | cpp11::list fn() { 184 | cpp11::writable::list x; 185 | x.push_back({"foo"_nm = 1}); 186 | return x; 187 | } 188 | [[cpp11::egister]] 189 | cpp11::list fn2() { 190 | cpp11::writable::list x; 191 | x.push_back({"foo"_nm = 1}); 192 | return x; 193 | }')) 194 | 195 | 196 | 197 | expect_error( 198 | cpp11::cpp_source( 199 | code = ' 200 | #include 201 | #include 202 | [[cpp11::link_to("progress")]] 203 | [[cpp11::register]] void show_progress() { 204 | RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 205 | pb.tick(0); 206 | for (int i = 0; i < 100; i++) { 207 | usleep(2.0 / 100 * 1000000); 208 | pb.tick(); 209 | } 210 | } 211 | ')) 212 | }) 213 | 214 | test_that("cpp_source(d) functions work after sourcing file more than once", { 215 | cpp11::cpp_source(test_path("single.cpp"), clean = TRUE) 216 | expect_equal(foo(), 1) 217 | cpp11::cpp_source(test_path("single.cpp"), clean = TRUE) 218 | expect_equal(foo(), 1) 219 | }) 220 | 221 | test_that("cpp_source fails informatively for nonexistent file", { 222 | i_do_not_exist <- tempfile(pattern = "nope-", fileext = ".cpp") 223 | expect_false(file.exists(i_do_not_exist)) 224 | expect_snapshot( 225 | error = TRUE, 226 | cpp_source(i_do_not_exist), 227 | transform = ~ sub("^.+[.]cpp$", "{NON_EXISTENT_FILEPATH}", .x) 228 | ) 229 | }) 230 | -------------------------------------------------------------------------------- /tests/testthat/test-utils.R: -------------------------------------------------------------------------------- 1 | describe("glue_collapse_data", { 2 | it("works with empty inputs", { 3 | expect_equal( 4 | glue_collapse_data(mtcars, ""), 5 | "" 6 | ) 7 | 8 | expect_equal( 9 | glue_collapse_data(mtcars[FALSE, ], "{hp}"), 10 | "" 11 | ) 12 | }) 13 | 14 | it("works with non-empty inputs", { 15 | expect_equal( 16 | glue_collapse_data(mtcars[1, ], "{hp}"), 17 | "110" 18 | ) 19 | 20 | expect_equal( 21 | glue_collapse_data(mtcars[1:2, ], "{hp}"), 22 | "110, 110" 23 | ) 24 | }) 25 | }) 26 | 27 | describe("stop_unless_installed", { 28 | mockery::stub(stop_unless_installed, "requireNamespace", FALSE) 29 | 30 | expect_error( 31 | stop_unless_installed("foo"), 32 | "The foo package\\(s\\) are required for this functionality" 33 | ) 34 | }) 35 | -------------------------------------------------------------------------------- /tests/testthat/test-vendor.R: -------------------------------------------------------------------------------- 1 | describe("cpp_vendor", { 2 | it("errors if cpp11 is not installed", { 3 | pkg <- local_package() 4 | mockery::stub(cpp_vendor, "system.file", "") 5 | expect_error( 6 | cpp_vendor(pkg_path(pkg)), 7 | "cpp11 is not installed" 8 | ) 9 | }) 10 | 11 | it("errors if cpp11 is already vendored", { 12 | pkg <- local_package() 13 | cpp_vendor(pkg_path(pkg)) 14 | 15 | expect_error( 16 | cpp_vendor(pkg_path(pkg)), 17 | "already exists" 18 | ) 19 | }) 20 | 21 | it("vendors cpp11", { 22 | pkg <- local_package() 23 | p <- pkg_path(pkg) 24 | 25 | cpp_vendor(pkg_path(pkg)) 26 | 27 | expect_true(dir.exists(file.path(p, "inst", "include", "cpp11"))) 28 | expect_true(file.exists(file.path(p, "inst", "include", "cpp11.hpp"))) 29 | expect_true(file.exists(file.path(p, "inst", "include", "cpp11", "declarations.hpp"))) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/growth.Rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lib/cpp11/05c888b0c6f49e7b252b79b028e4719e6d5b299d/vignettes/growth.Rds -------------------------------------------------------------------------------- /vignettes/release.Rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lib/cpp11/05c888b0c6f49e7b252b79b028e4719e6d5b299d/vignettes/release.Rds -------------------------------------------------------------------------------- /vignettes/sum.Rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lib/cpp11/05c888b0c6f49e7b252b79b028e4719e6d5b299d/vignettes/sum.Rds --------------------------------------------------------------------------------