Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to "grab"/"scrape" the .Call() output of C printf() within R (or with Rprintf())?

Tags:

c

string

r

I'm working with R code that has an interface to C via .Call(). The C function outputs to STDOUT via printf(). As a concrete, more simple example, I'll follow http://mazamascience.com/WorkingWithData/?p=1099

Here is our C code with outputs hello world via printf(), helloA1.c:

#include <R.h>
#include <Rdefines.h>
#include <stdio.h>
SEXP helloA1() {
  printf("Hello World!\n");
  return(R_NilValue);
}

After installation via R CMD SHLIB helloA1.c, we invoke the function as follows within R:

> dyn.load("helloA1.so")
> hellocall = .Call("helloA1")
  Hello World!

I cannot access the text "Hello World!" within R as a data structure though. e.g.

> vec1 = as.vector( .Call("helloA1"))
Hello World!
> vec1
NULL
> 

or

> library(data.table)
> dt = as.data.table(.Call("helloA1"))
Hello World!
> dt
Null data.table (0 rows and 0 cols)
> 

Is there any way to "load" the output of printf() into R?

I can translate the functions into Rcpp, but will run into the same problem I think with Rprintf().

EDIT: Apologies, I had previously thought RPrintf() was a function within Rcpp. I have edited the title of this question appropriately.

like image 924
ShanZhengYang Avatar asked Nov 16 '25 23:11

ShanZhengYang


1 Answers

So, the issue here is printf negates R's built collection mechanisms for output. In particular, there is no C-level 'stdout' file stream and, thus, no output to collect in either Rgui or RStudio. For details, please see Section 6.5 Printing of Writing R Extensions

Two possible solutions:

  1. Define a macro that sets printf to direct into Rprintf and include the #define STRICT_R_HEADERS to avoid errors.
  2. Switch instances of printf to Rprintf in the offending code.

From here, the capture can be passed off to either capture.output(), which directly assigns output to a variable, or sink(), which redirects output to a file whose contents must then be read back in using readLines(). The latter makes it possible to have a clear enclose over multiple lines of code to capture output while the prior is focused on securing output present from an inputted expression.

Option 1

For the first iteration, just define a header that includes the custom define and then include the third party library, e.g.

my_code.h

#ifndef MY_CODE_H
#define MY_CODE_H
#include <R.h>
// this load R_ext/Print.h.

// #include <YOUR_LIBRARY.h>

// Define strict headers
#define STRICT_R_HEADERS
// Map printf to Rprintf
#define printf Rprintf
#endif

toad.c

#include <R.h>
#include <Rdefines.h>
#include "my_code.h"

SEXP helloA1() {
  printf("Hello World!\n");
  return(R_NilValue);
}

toad_example.R

system("R CMD SHLIB ~/Desktop/toad.c")
dyn.load("~/Desktop/toad.so")

helloA1 <- function() {
  result <- .Call("helloA1")
}

# Gregor's suggestion
captured_data = capture.output(helloA1())

# Using sink around multiple function calls to redirect output
# to a single file
sink("sink-examp.txt")
helloA1()
sink()

input_data = readLines("sink-examp.txt")

all.equal(input_data, captured_data)
# [1] TRUE

I've implemented this approach in an R package that can be found here:

https://github.com/coatless/printf2Rprintf

Option 2

This option manually redefines the printf functions.

toad.c

#include <R.h>
#include <Rdefines.h>
SEXP helloA1() {
  Rprintf("Hello World!\n"); // manually changed
  return(R_NilValue);
}

toad_example.R

system("R CMD SHLIB ~/Desktop/toad.c")
dyn.load("~/Desktop/toad.so")

helloA1 <- function() {
  result <- .Call("helloA1")
}

# Gregor's suggestion
captured_data = capture.output(helloA1())

# Using sink around multiple function calls to redirect output
# to a single file
sink("sink-examp.txt")
helloA1()
sink()

input_data = readLines("sink-examp.txt")

all.equal(input_data, captured_data)
# [1] TRUE
like image 109
coatless Avatar answered Nov 18 '25 12:11

coatless



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!