Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
114 views
in Technique[技术] by (71.8m points)

c++ - 'unlockEnvironment' implemented via 'Rcpp' instead of 'inline'

Actual question

Could someone get me started on what I need to do to implement the code of unlockEnvironment below in Rcpp?

Background

Came across this post and tried Winston Chang's solution based on C code with inline. It works, but I have the feeling I know too little (practically nothing, that is) about either inline or C/C++ to really know what I'm doing ;-)

So I thought this would be a great opportunity to finally start learning on how to use R as an interface to C and C++. And I think I'd like to hopp on the Rcpp train for doing so!

The code from Winston's Gist

require("inline")

inc <- '
/* This is taken from envir.c in the R 2.15.1 source
https://github.com/SurajGupta/r-source/blob/master/src/main/envir.c
*/
#define FRAME_LOCK_MASK (1<<14)
#define FRAME_IS_LOCKED(e) (ENVFLAGS(e) & FRAME_LOCK_MASK)
#define UNLOCK_FRAME(e) SET_ENVFLAGS(e, ENVFLAGS(e) & (~ FRAME_LOCK_MASK))
'
src <- '
if (TYPEOF(env) == NILSXP)
error("use of NULL environment is defunct");
if (TYPEOF(env) != ENVSXP)
error("not an environment");

UNLOCK_FRAME(env);

// Return TRUE if unlocked; FALSE otherwise
SEXP result = PROTECT( Rf_allocVector(LGLSXP, 1) );
LOGICAL(result)[0] = FRAME_IS_LOCKED(env) == 0;
UNPROTECT(1);

return result;
'
unlockEnvironment <- inline::cfunction(
  signature(env = "environment"),
  includes = inc,
  body = src
)

Refactoring error

On a side note: I ran into an error with Winston's code when I organize it in a certain way in the /R directory of my package project:

Using S4 methods most of the time, I tried to factor Winston's code out into a standard R function .unlockEnvironment() that I put into the file /R/.unlockEnvironment.r

I then would create my S4 methods for unlockEnvironment() in /R/unlockEnvironment.r. The method with signature env:environment would then simply call .unlockEnvironment(env = env).

Setting up things that way, I end up with the following error:

Error in .Primitive(".Call")(, env) : NULL value passed as symbol address

If I put the code in /R/.unlockEnvironment.r directory with in the respective method in /R/unlockEnvironment.r (thus re-sourcing the inline code each time the respective method of unlockEnvironment() is called), everything works just fine - but it's very inefficient because of the repeated re-sourcing.

So I guess this must have either something to do with the way the C code is written or with the way you need to organize your C-based functions when using inline?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It sounds like your question basically amounts to, 'how do I use Rcpp::attributes'? And I would suggest that you go over many of the examples in the Rcpp Gallery to learn a bit more.

The main idea: write some C++ code, put it a .cpp file, then call Rcpp::sourceCpp(<file>) to load it. For this particular example:

#include <Rcpp.h>
using namespace Rcpp;

/* This is taken from envir.c in the R 2.15.1 source
https://github.com/SurajGupta/r-source/blob/master/src/main/envir.c
*/
#define FRAME_LOCK_MASK (1<<14)
#define FRAME_IS_LOCKED(e) (ENVFLAGS(e) & FRAME_LOCK_MASK)
#define UNLOCK_FRAME(e) SET_ENVFLAGS(e, ENVFLAGS(e) & (~ FRAME_LOCK_MASK))

// [[Rcpp::export]]
bool unlock_environment(Environment env) {
  UNLOCK_FRAME(env);
  return FRAME_IS_LOCKED(env) == 0;
}

/*** R
env <- new.env()
lockEnvironment(env)
try(env$a <- 1) ## error
unlock_environment(env)
env$a <- 1
*/

Calling Rcpp::sourceCpp() on a file with these contents gives me:

> Rcpp::sourceCpp('~/scratch/unlock.cpp')

> env <- new.env()

> lockEnvironment(env)

> try(env$a <- 1) ## error
Error in env$a <- 1 : cannot add bindings to a locked environment

> unlock_environment(env)
[1] TRUE

> env$a <- 1 ## success!

The main little features here:

  1. You can provide signatures using base / STL C++ types, or Rcpp types as well. Note that bool is the C++ bool type, and Environment is an Rcpp type encompassing environments.
  2. Rcpp Attributes handles automatic conversion of these C++ return types to R's SEXP.

You might like Hadley's adv-r introduction as well.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...