Skip to content

How do I access class slots from C?

12 messages · Simon Urbanek, Martin Morgan, Dirk Eddelbuettel +2 more

#
Hi,

There is a GET_SLOT macro in Rdefines.h

Cheers,

Romain
On 09/29/2009 04:28 PM, Abhijit Bera wrote:

  
    
#
Abhijit,

as for your subject - it's GET_SLOT,
but why don't you just use ParseVector and eval instead of hand- 
crafting C code that calls the evaluator? That latter is way more  
error prone and the error-handling is a nightmare (your current code  
is inefficient anyway so you don't gain anything).

As for setWeights, you got the code wrong - if you want to mimic the R  
code then it's a call to the assignment "<-" - have a look at the  
parse result of
"setWeights(ewSpec) <- rep(1/nAssets, times = nAssets)":

   @d58774 06 LANGSXP g0c0 []
     @809008 01 SYMSXP g1c0 [MARK,gp=0x4000] "<-"
     @d59540 06 LANGSXP g0c0 []
       @1a1af34 01 SYMSXP g0c0 [] "setWeights"
       @d59498 01 SYMSXP g0c0 [] "ewSpec"
     @d58720 06 LANGSXP g0c0 []
       @814ac4 01 SYMSXP g1c0 [MARK,gp=0x4000] "rep"
       @d595b0 06 LANGSXP g0c0 []
	@80ae44 01 SYMSXP g1c0 [MARK,gp=0x4000] "/"
	@1bf8ce8 14 REALSXP g0c1 [] (len=1, tl=0) 1
	@1dbf1ac 01 SYMSXP g0c0 [MARK] "nAssets"
       TAG: @9450fc 01 SYMSXP g1c0 [MARK] "times"
       @1dbf1ac 01 SYMSXP g0c0 [MARK] "nAssets"

Again, I think you would be far better off just using parse instead...

Cheers,
Simon

PS: Your PROTECTs are way off-balance, and you don't need almost any  
of them - langX and listX protect all arguments
On Sep 29, 2009, at 10:28 , Abhijit Bera wrote:

            
#
Simon Urbanek wrote:
My 2 cents: constructing language calls seems much more appropriate than 
parsing expressions when the values to be used are from programming 
variables, as is likely the case in most real applications? And 
accessing slots with GET_SLOT seems to break the (hard-won) abstraction 
layer of S4, so better to call the R accessors (even when this is 
inefficient computationally) especially when using objects from packates 
whose internals you do not control.

One particular part of the code below

PROTECT(e=lang4(install("c"),mkString("SBI"),mkString("SPI"),mkString("SII"))); 


could be re-written as

   SEXP x = PROTECT(NEW_CHARACTER(3));
   SET_STRING_ELT(x, 0, mkChar("SBI"));
   etc

In the original, although the outer PROTECT is unnecessary, I wonder 
about the mString()... calls, which are not guaranteed to be evaluated 
in order and produce unprotected CHARSXPs (prior to being protected 
inside lang4). I'm not really sure about the mkChar() in the above, 
either, and whether there is an opportunity for garbage collection in 
SET_VECTOR_ELT.

Martin

  
    
#
MArtin,
On Sep 29, 2009, at 12:17 , Martin Morgan wrote:

            
Yes, but not in this case - it's all just replication of a script that  
could be evaluated with one call, there are no native objects  
involved. As soon as you start using more than one eval in the C code,  
it is very likely not the right way to go. I do agree that constructed  
expressions are useful if you have a) a very short call that cannot be  
easily done at C level or b) it involves  unassigned objects generated  
in the C code -- but neither it the case here.
Agreed, again something that could be prevented with parse + eval ;).
CHARSXPs are always protected by enclosing STRSXP and  
SET_STRING_ELT(x, i, mkChar()) is always safe. And yes, the lang4 "c"  
construct is a bad idea.

Cheers,
Simon
#
This is so much fun.  The C code posted wasn't exactly legible.  So here is a
new C++ variant that I just committed to the RInside SVN as a new example.
And it mine works (against RInide and Rcpp as on CRAN):

edd at ron:~/svn/rinside/pkg/inst/examples> ./rinside_sample4
Package 'sn', 0.4-12 (2009-03-21). Type 'help(SN)' for summary information
Using the GLPK callable library version 4.37

Title:
 MV Feasible Portfolio 
 Estimator:         covEstimator 
 Solver:            solveRquadprog 
 Optimize:          minRisk 
 Constraints:       LongOnly 

Portfolio Weights:
SBI SPI SII LMI MPI ALT 
0.1 0.1 0.1 0.1 0.3 0.3 

Covariance Risk Budgets:
    SBI     SPI     SII     LMI     MPI     ALT 
-0.0038  0.1423  0.0125 -0.0058  0.4862  0.3686 

Target Return and Risks:
  mean     mu    Cov  Sigma   CVaR    VaR 
0.0548 0.0548 0.4371 0.4371 1.0751 0.6609 

Description:
 Tue Sep 29 13:43:36 2009 by user:  
             SBI	-0.00380065
             SPI	   0.142261
             SII	  0.0125242
             LMI	-0.00576251
             MPI	   0.486228
             ALT	   0.368551
edd at ron:~/svn/rinside/pkg/inst/examples> 

The final few lines are C++ accessing the result, earlier in the code I
assign the weight vector from C++ as you desired from C.  All with error
checking / exception handling and what have in under 60 lines of (IMHO more
readable) code -- see below.

Dirk

// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4;  tab-width: 8; -*-
//
// Another simple example inspired by an r-devel mail by Abhijit Bera
//
// Copyright (C) 2009 Dirk Eddelbuettel and GPL'ed 

#include "RInside.h"                    // for the embedded R via RInside
#include "Rcpp.h"                       // for the R / Cpp interface used for transfer
#include <iomanip>

int main(int argc, char *argv[]) {

    try {
        RInside R(argc, argv);          // create an embedded R instance 
        SEXP ans;

        std::string txt = "suppressMessages(library(fPortfolio))";
        if (R.parseEvalQ(txt))          // load library, no return value
            throw std::runtime_error("R cannot evaluate '" + txt + "'");

        txt = "lppData <- 100 * LPP2005.RET[, 1:6]; "
	  "ewSpec <- portfolioSpec(); " 
	  "nAssets <- ncol(lppData); ";
        if (R.parseEval(txt, ans))      // prepare problem
            throw std::runtime_error("R cannot evaluate '" + txt + "'");
	
	const double dvec[6] = { 0.1, 0.1, 0.1, 0.1, 0.3, 0.3 }; // choose any weights you want
	const std::vector<double> w(dvec, &dvec[6]);

	R.assign( w, "weightsvec");	// assign STL vector to R's 'weightsvec' variable

	txt = "setWeights(ewSpec) <- weightsvec";
        if (R.parseEvalQ(txt))		// evaluate assignment
            throw std::runtime_error("R cannot evaluate '" + txt + "'");

	txt = "ewPortfolio <- feasiblePortfolio(data = lppData, spec = ewSpec, constraints = \"LongOnly\"); "
	  "print(ewPortfolio); "
	  "vec <- getCovRiskBudgets(ewPortfolio at portfolio)";
        if (R.parseEval(txt, ans))      // assign covRiskBudget weights to ans
            throw std::runtime_error("R cannot evaluate '" + txt + "'");
	RcppVector<double> V(ans);      // convert SEXP variable to an RcppMatrix
  
	R.parseEval("names(vec)", ans);	// assign columns names to ans
	RcppStringVector names(ans);   

	for (int i=0; i<names.size(); i++) {
	  std::cout << std::setw(16) << names(i) << "\t"
		    << std::setw(11) << V(i) << "\n";
        }
        
    } catch(std::exception& ex) {
        std::cerr << "Exception caught: " << ex.what() << std::endl;
    } catch(...) {
        std::cerr << "Unknown exception caught" << std::endl;
    }

    exit(0);
}
#
On 09/30/2009 08:51 AM, Abhijit Bera wrote:
What's in the database ? Is this the data or the R code ? What's wrong 
with writing your own set of R functions and evaluate calls to these 
functions instead of basically replicate this in C or C++ or whatever.

Dirk's code certainly is nicer, but would you really do it like that in 
real life ?

Romain

  
    
#
On 09/30/2009 09:45 AM, Abhijit Bera wrote:
Right, so the data is in the database.
Sure. My point is (take it or leave it) since you are already using some 
R packages (fPortfolio, etc ...) why don't you just write one that 
contains a set of simple utility functions that are only aimed at 
simplifying your C code, and call these from C.

I'd find this much easier to code, document and test ... but eventually 
you should do what __you__ find easier/better

Romain

  
    
#
On 09/30/2009 09:51 AM, Abhijit Bera wrote:
Essentially you are not using a C only approach, even in your original 
example, since you call functions from fPortfolio (btw, R has to look 
down the search path each time to find the function, so you can make 
this more efficient, ...)

It might even be faster like this since you only need to parse the R 
functions once, so you pay the (not so expensive btw) price of parsing 
once.

Combine this with an approach that caches the references to the 
functions, so you only pay the price of searching through R search path 
once, and you're set.

Anyway, chances are the code from fPorfolio you call will be what costs 
the most, after all that is what is actually working here. Before 
converting your R script to C, did you do some profiling to identify 
what takes the most time ?

Romain