File: later-cpp.Rmd

package info (click to toggle)
r-cran-later 1.1.0.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 412 kB
  • sloc: cpp: 1,473; ansic: 1,096; sh: 29; makefile: 2
file content (112 lines) | stat: -rw-r--r-- 4,321 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
---
title: "Using later from C++"
author: "Joe Cheng"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using later from C++}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

# Using later from C++

You can call `later::later` from C++ code in your own packages, to cause your own C-style functions to be called back. This is safe to call from either the main R thread or a different thread; in both cases, your callback will be invoked from the main R thread.

To use the C++ interface, you'll need to:

* Add `later` to your `DESCRIPTION` file, under both `LinkingTo` and `Imports`
* Make sure that your `NAMESPACE` file has an `import(later)` entry. If your package uses roxygen2, you can do this by adding the following lines to any file under `R/`:
```
#' @import later
NULL
```
* Add `#include <later_api.h>` to the top of each C++ file that uses the below APIs.

## Executing a C function later

The `later::later` function is accessible from `later_api.h` and its prototype looks like this:

```cpp
void later(void (*func)(void*), void* data, double secs)
```

The first argument is a pointer to a function that takes one `void*` argument and returns void. The second argument is a `void*` that will be passed to the function when it's called back. And the third argument is the number of seconds to wait (at a minimum) before invoking. In all cases, the function will be invoked on the R thread, when no user R code is executing.

## Background tasks

This package also offers a higher-level C++ helper class called `later::BackgroundTask`, to make it easier to execute tasks on a background thread. It takes care of launching the background thread for you, and returning control back to the R thread at a later point; you're responsible for providing the actual code that executes on the background thread, as well as code that executes on the R thread before and after the background task completes.

Its public/protected interface looks like this:

```cpp
class BackgroundTask {

public:
  BackgroundTask();
  virtual ~BackgroundTask();

  // Start executing the task  
  void begin();

protected:
  // The task to be executed on the background thread.
  // Neither the R runtime nor any R data structures may be
  // touched from the background thread; any values that need
  // to be passed into or out of the Execute method must be
  // included as fields on the Task subclass object.
  virtual void execute() = 0;
  
  // A short task that runs on the main R thread after the
  // background task has completed. It's safe to access the
  // R runtime and R data structures from here.
  virtual void complete() = 0;
}
```

Create your own subclass, implementing a custom constructor plus the `execute` and `complete` methods.

It's critical that the code in your `execute` method not mutate any R data structures, call any R code, or cause any R allocations, as it will execute in a background thread where such operations are unsafe. You can, however, perform such operations in the constructor (assuming you perform construction only from the main R thread) and `complete` method. Pass values between the constructor and methods using fields.

```rcpp
#include <Rcpp.h>
#include <later_api.h>

class MyTask : public later::BackgroundTask {
public:
  MyTask(Rcpp::NumericVector vec) :
    inputVals(Rcpp::as<std::vector<double> >(vec)) {
  }

protected:
  void execute() {
    double sum = 0;
    for (std::vector<double>::const_iterator it = inputVals.begin();
      it != inputVals.end();
      it++) {
      
      sum += *it;
    }
    result = sum / inputVals.size();
  }
  
  void complete() {
    Rprintf("Result is %f\n", result);
  }

private:
  std::vector<double> inputVals;
  double result;
};
```

To run the task, `new` up your subclass and call `begin()`, e.g. `(new MyTask(vec))->begin()`. There's no need to keep track of the pointer; the task object will delete itself when the task is complete.

```r
// [[Rcpp::export]]
void asyncMean(Rcpp::NumericVector data) {
  (new MyTask(data))->begin();
}
```

It's not very useful to execute tasks on background threads if you can't get access to the results back in R. We'll soon be introducing a complementary R package that provides a suitable "promise" or "future" abstraction.