3 votes

Est-il possible de "saisir"/"gratter" la sortie .Call() de printf() en C dans R (ou avec Rprintf()) ?

Je travaille avec du code R qui a une interface vers le C via .Call() . La fonction C sort vers STDOUT via printf() . Pour un exemple concret et plus simple, je vais suivre http://mazamascience.com/WorkingWithData/?p=1099

Voici notre code C avec des sorties hello world via printf() , helloA1.c :

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

Après l'installation via R CMD SHLIB helloA1.c nous invoquons la fonction comme suit dans R :

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

Je ne peux pas accéder au texte "Hello World !" dans R en tant que structure de données, par exemple.

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

ou

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

Existe-t-il un moyen de "charger" la sortie de l'application printf() en R ?

Je peux traduire les fonctions en Rcpp, mais je rencontrerai le même problème, je pense, avec Rprintf() .

EDIT : Mes excuses, j'avais pensé précédemment que RPrintf() était une fonction de Rcpp . J'ai modifié le titre de cette question en conséquence.

4voto

coatless Points 10952

Donc, le problème ici est printf nie R Les mécanismes de collecte construits par l'UE pour les résultats. En particulier, il n'y a pas de flux de fichiers 'stdout' au niveau C et, par conséquent, pas de sortie à collecter dans Rgui ou RStudio. Pour plus de détails, veuillez consulter Section 6.5 Impression de Écriture d'extensions R

Deux solutions possibles :

  1. Définissez une macro qui définit printf à diriger vers Rprintf et inclure le #define STRICT_R_HEADERS pour éviter les erreurs .
  2. Changez les instances de printf a Rprintf dans le code incriminé.

À partir de là, la capture peut être transmise à l'un ou l'autre des éléments suivants capture.output() qui affecte directement la sortie à une variable, ou bien sink() qui redirige la sortie vers un fichier dont le contenu doit ensuite être relu à l'aide de la commande readLines() . Ce dernier permet d'avoir une enveloppe claire sur plusieurs lignes de code pour capturer la sortie, tandis que le précédent se concentre sur la sécurisation de la sortie présente à partir d'une expression entrée.

Option 1

Pour la première itération, il suffit de définir un en-tête qui comprend la définition personnalisée, puis d'inclure la bibliothèque tierce, par exemple

mon_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

crapaud.c

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

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

exemple de crapaud.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

J'ai implémenté cette approche dans un paquet R que vous pouvez trouver ici :

https://github.com/coatless/printf2Rprintf

Option 2

Cette option redéfinit manuellement le printf fonctions.

crapaud.c

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

exemple de crapaud.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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X