[Rcpp-devel] rcpp_modules: Passing object pointer back to C++
Jelmer Ypma
jelmerypma at gmail.com
Sun Jan 22 16:18:05 CET 2012
Hi Michael,
I ran into a similar problem this week when trying to wrap/as a class
that is exposed to R using modules. In the end, the example below
solved my problem, although it's probably not the most efficient
solution.
If I understand your question correctly, you probably want something
similar to what's implemented in as<TestClass> below. The pointer to
the underlying C++ class is obtained by Rcpp::XPtr<TestClass> xptr(
env.get(".pointer") ). You can then use xptr to access the function in
the class, copy the values of members, etc. Probably you can also
convert it to a raw pointer if that's what you need, but I'm not sure
whether that would be safe.
I hope this helps at least somewhat.
Best wishes,
Jelmer
====================
library('inline')
library('Rcpp')
# Declare TestClass class and Rcpp::wrap, and Rcpp::as
TestClassForwardCode <- '
#include <RcppCommon.h>
// declare TestClass class
class TestClass
{
public:
TestClass( int i );
int const value() const;
private:
int d_val;
// The program does not compile, if we make this copy
constructor private.
// explicit TestClass(const TestClass& original);
};
// declaring the specialization
namespace Rcpp {
template <> SEXP wrap( const TestClass& );
template <> TestClass as( SEXP ) throw(not_compatible);
}
// this must appear after the specialization,
// otherwise the specialization will not be seen by Rcpp types
'
# Implementation of as and wrap code
TestClassAsWrapCode <- '
namespace Rcpp {
template <> SEXP wrap<TestClass>( const TestClass& el ) {
int val = el.value();
Rcpp::Language call( "new", Symbol( "TestClass" ), val ) ;
return call.eval() ;
};
template <> TestClass as<TestClass>( SEXP s ) throw(not_compatible) {
try {
if ( TYPEOF(s) != S4SXP ) {
::Rf_error( "supplied object is not of type TestClass." );
}
Rcpp::S4 s4obj( s );
if ( !s4obj.is("Rcpp_TestClass" ) ) {
::Rf_error( "supplied object is not of type TestClass." );
}
Rcpp::Environment env( s4obj );
Rcpp::XPtr<TestClass> xptr( env.get(".pointer") );
// build new TestClass object with copied data
return TestClass( xptr->value() );
}
catch(...) {
::Rf_error( "supplied object could not be converted to
TestClass." );
}
};
}
'
# Implementation of TestClass class and two functions
# that require wrap<TestClass> or as<TestClass>
TestClassImplementation <- '
// implementation of TestClass class
TestClass::TestClass( int i ) : d_val( i ) {}
int const TestClass::value() const
{
return d_val;
}
// definition and implementation of function using TestClass
int useTestClass( TestClass ee ) {
Rcpp::Rcout << "Multiply value in TestClass by 2." << std::endl;
return 2 * ee.value();
}
// definition and implementation of function returning TestClass
TestClass returnTestClass( int val ) {
Rcpp::Rcout << "Create and return a TestClass." << std::endl;
return TestClass( val );
}
'
# Implementation of module code
TestModuleCode <- '
RCPP_MODULE(Test_module) {
// expose TestClass class
class_<TestClass>( "TestClass" )
.constructor<int>()
.method( "value", &TestClass::value )
;
function( "useTestClass", &useTestClass );
function( "returnTestClass", &returnTestClass );
}
'
# Create plugin to add wrap and as for TestClass
plug <- function(){
settings <- getPlugin( "Rcpp" )
settings$includes <- paste( TestClassForwardCode,
settings$include, TestClassAsWrapCode )
return( settings )
}
registerPlugin( "RcppTestWrap", plug )
# Compile module, and extract classes TestClass and Container
test_mod <- cxxfunction( signature(),
body = "",
include = paste(TestClassImplementation,
TestModuleCode, sep='\n'),
plugin = "RcppTestWrap", verbose=TRUE )
Test_module <- Module( "Test_module", getDynLib(test_mod) )
TestClass <- Test_module$TestClass
useTestClass <- Test_module$useTestClass
returnTestClass <- Test_module$returnTestClass
# Create new TestClass
obj1 <- new( TestClass, 5 )
obj1$value()
useTestClass( obj1 )
obj2 <- returnTestClass( 4 )
obj2$value()
useTestClass( obj2 )
====================
On Sun, Jan 22, 2012 at 14:43, <Mr.M at gmx.at> wrote:
> Hello there,
>
> I am implementing a package with rcpp_modules which basically worked fine until I tried to add a method which allows the user to do the following in R:
>
> a = new (MyPackage::MyClass)
> b = new (MyPackage::MyClass)
> a.doSomething(b)
>
> -------
>
> The code behind looks somewhat like this:
>
> #include <Rcpp.h>
> class MyClass {
> public:
>
> int a;
>
> MyClass(int a_){a = a_};
>
> //Option1
> void doSomething(MyClass * ref){
> return ref->a + a;
> }
> //Option2
> void doSomething(SEXP sx){
> MyClass * ref = //what to write here???
> return ref->a + a;
> }
> //End
>
> };
>
>
> RCPP_MODULE(yada) {
> class_<Uniform>( "Uniform" )
> .constructor<int>()
> .field( "a", &MyClass::a )
> .method( "doSomething", &MyClass::doSomething )
> ;
> }
>
> ----------
>
> So, what I am trying to do, is to get a C++ pointer to the (C++) object of the method-argument.
>
> Option 1 does not work, because the MyClass * is not an Rcpp compatible type:
> "error: cannot convert ‘SEXP’ to ‘MyClass*’ in initialization"
>
> With Option 2 I have not figured out, how to cast the SEXP to something useful. It definitely is a S4SXP with one slot ".xData". The contents of this slot are something like "<environment: 0x2b2f830>".
>
> So my question now is: Is this the memory address of the C++ object?
> If it is, how can I assign this address to a C++ pointer of type "MyClass *" ?
> Is there any other possibility to unwrap rcpp_module's S4SXP datatypes to C++ pointers?
>
> (I am aware that R garbage-collects the object when it goes out of scope; This is no problem in this scenario)
>
> ------
>
> So far I have (unsuccessfully) tried:
>
> MyClass *ref = static_cast<MyClass*> (R_ExternalPtrAddr(n));
>
> if( TYPEOF( sx ) == S4SXP ) MyClass* ref = (MyClass*) EXTPTR_PTR( R_do_slot( sx , Rf_install(".xData") ) ) ;
>
> if( TYPEOF( sx ) == S4SXP ) MyClass* ref = (MyClass*) R_do_slot( sx , Rf_install(".xData") );
>
> Rcpp::S4 a = Rcpp::S4(sx); MyClass* ref = (MyClass*) a.slot(".xData")<MyClass*>;
>
>
> The first three result in a memory error. The last one is syntactically incorrect, because I have not figured out how to correctly use the template operator of SlotProxy.
>
> -------
>
> A somewhat related thread is: http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2011-December/003223.html
>
> Any help is appreciated.
> (The only thing that mustn't be changed is "a.doSomething(b)".)
>
>
> regards, Michael
>
> By the way... I think that rcpp_modules is a great tool and will be widely used in future.
>
> --
> "Feel free" - 10 GB Mailbox, 100 FreeSMS/Monat ...
> Jetzt GMX TopMail testen: http://www.gmx.net/de/go/topmail
> _______________________________________________
> Rcpp-devel mailing list
> Rcpp-devel at lists.r-forge.r-project.org
> https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
More information about the Rcpp-devel
mailing list