Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: rcpp code chunk compilation of {.h, .cpp} pairs or .cpp files that include other user-defined header files #2367

Open
3 tasks done
astamm opened this issue Oct 2, 2024 · 5 comments

Comments

@astamm
Copy link

astamm commented Oct 2, 2024

Currently, an Rcpp code chunk expects a single standalone .cpp file.

Compilation of {.h, .cpp} pairs

If you have a habit of separating function declaration in a header file with
.h extension and function implementation in the eponymous .cpp file, then
including the cpp file or the .h file in a Rcpp code chunk will result in
failure to compile. This is because Rcpp code chunks make use of
Rcpp::sourceCpp() function, which only accepts a single .cpp file by design.

Compilation of .cpp files that include other user-defined header files

If you have multiple files to include in a single Rcpp code chunk, then you
cannot include them directly in the Rcpp code chunk. This is because, behind the
scene, Rcpp::sourceCpp() creates a -- possibly temporary -- cache directory
into which it copies only the single input file provided in the file argument.

Workarounds

I have found workarounds for the two above mentioned problems which I made
a repository of to experiment: https://github.com/astamm/rcpp-code-cell-processing.

In short:

  • Workaround to 1st issue: You should convert every pair of file (myfile.h, myfile.cpp)
    into a single file (myfile.hpp) and then include the .hpp file in the Rcpp code chunk.
  • Workaround to 2nd issue: You can manually copy all the files you want to include in the
    Rcpp code chunk to the cache directory created by Rcpp::sourceCpp(). This can
    be done by setting the cacheDir argument of the Rcpp::sourceCpp() function
    to the desired cache directory via the cacheDir optional argument. Behind the
    scene, Rcpp::sourceCpp() subsequently creates a sub-directory in that
    directory whose name is platform-dependent and retrieved via
    Rcpp:::.sourceCppPlatformCacheDir(). We can therefore decide beforehand of the
    location of that cache directory, create it and copy the files we want to
    include in the Rcpp code chunk to that directory.

Could it be a nice feature to have this directly handled by knitr upon knitting?

Thanks!


By filing an issue to this repo, I promise that

  • I have fully read the issue guide at https://yihui.org/issue/.
  • I have provided the necessary information about my issue.
    • If I'm asking a question, I have already asked it on Stack Overflow or RStudio Community, waited for at least 24 hours, and included a link to my question there.
    • If I'm filing a bug report, I have included a minimal, self-contained, and reproducible example, and have also included xfun::session_info('knitr'). I have upgraded all my packages to their latest versions (e.g., R, RStudio, and R packages), and also tried the development version: remotes::install_github('yihui/knitr').
    • If I have posted the same issue elsewhere, I have also mentioned it in this issue.
  • I have learned the Github Markdown syntax, and formatted my issue correctly.

I understand that my issue may be closed if I don't fulfill my promises.

@cderv
Copy link
Collaborator

cderv commented Oct 2, 2024

Thanks for the feedback.

Just pointing out that the engine is defined here

knitr/R/engine.R

Lines 292 to 314 in 2dc1e4a

eng_Rcpp = function(options) {
sourceCpp = getFromNamespace('sourceCpp', 'Rcpp')
code = one_string(options$code)
# engine.opts is a list of arguments to be passed to Rcpp function, e.g.
# engine.opts=list(plugin='RcppArmadillo')
opts = options$engine.opts
# use custom cacheDir for sourceCpp if it's supported
cache = options$cache && ('cacheDir' %in% names(formals(sourceCpp)))
if (cache) {
opts$cacheDir = paste(valid_path(options$cache.path, options$label), 'sourceCpp', sep = '_')
opts$cleanupCacheDir = TRUE
}
if (!is.environment(opts$env)) opts$env = knit_global() # default env is knit_global()
if (options$eval) {
message('Building shared library for Rcpp code chunk...')
do.call(sourceCpp, c(list(code = code), opts))
}
engine_output(options, code, '')
}

As always, PR welcome if you wish to contribute. Otherwise, we'll consider this for future development probably. I'll let @yihui comment on this.

@yihui
Copy link
Owner

yihui commented Oct 2, 2024

I don't understand C++ or Rcpp well enough, but have you tried to input a vector of file paths to the chunk option file? This option doesn't have to take a single path, but can take an arbitrary number of files, e.g.,

#| file: ["foo.h", "foo.cpp"]

or

```{Rcpp, file = c("foo.h", "foo.cpp")}
```

@eternal-flame-AD
Copy link

eternal-flame-AD commented Oct 2, 2024

Had the same issue, but my reasoning was since I already bothered to extract the header file I would just write the actual code in a file too, and use a hidden manual sourceCpp() chunk using the actual file name to get around this.

It could be nicer but I think this serves the intended purpose: a code chunk is not designed to pull in code from elsewhere.

@yihui This will compile two things separated unfortunately.

@yihui
Copy link
Owner

yihui commented Oct 4, 2024

Okay, again I'm not an expert on this, but will be happy to review a PR if it's not too complicated. Thank you!

@astamm
Copy link
Author

astamm commented Oct 4, 2024

Working on it, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants