Comments (12)
@pierreblavy2 Dirk's advice will save you a lot of time: before trying to implement fancy things in C++, please take a look at the R internals and how the interface looks like. Some quick hints:
- Everything in R core is a
SEXP
, which is a lightweight self-describing structure. - One of this
SEXP
s is theEXTPTRSXP
, or external pointer. This just holds a void C pointer that could be anything. - So forget about unique pointers, shared pointers, and other fancy things: whenever you want to export some object to the R side, that must be a void C pointer.
- The other piece of the puzzle that Rcpp provides to seamlessly work with external pointers is the
XPtr
class. This allows you not only to cast them to the proper class and use them, but also to automatically register the removal of that object when the external pointer is no longer referenced in any place (so it is similar to a shared pointer).
Now, you are looking at this issue from the perspective of your particular use case, where you have a long-lived object that takes care of everything else, so you don't want the R garbage collector to fiddle with your objects, which is fine. But that's not the only use case, and given how old Rcpp is and the number of packages using it, I'd confidently say it's not the most common case. That's why the XPtr
constructor has a default bool set_delete_finalizer = true
. Otherwise, we would see tons of memory leaks in the dependent packages, because developers typically create objects, export them to R and expect that R takes care of cleaning things up when they don't use them anymore.
You seem to have a special advanced use case. So do I. My recommendation would be to forget about modules, and work with XPtr
directly. In my code, you'll find examples of long-lived objects that are properly garbage-collected when the reference is lost in the R session, as well as references to nodes in a linked list that need to be left alone (note the second argument set to false
).
Modules are much more complicated, and have several limitations. That said, once you are familiar with XPtr
s, if you feel like there's a way to improve modules, contributions are most welcome, and we can help you. :)
from rcpp.
Thanks for taking the time to write an issue. Rcpp is an R package using the .Call() interface using SEXP ("S Expression Pointers") so there are limits to what can be done here. If you have an idea to fundamentally improve upon this somewhat time-test solutions we'd be all ears and would welcome a PR or issue demonstrating it.
from rcpp.
Also, as an addendum, your example uses Rcpp Modules which are one of several ways to work with Rcpp. Modules offers a few nice features, but also have limits ... and in essence have not seen much development in close to a decade.
R offers external pointers, for Rcpp these become the XPtr object. You could play with those but because R itself is garbage collected in it is fairly easy to get entangled when mixing R object with C++ reference counted objects. But you may well have fresh ideas so I encourage to explore and express them.
from rcpp.
@pierreblavy2 I have been at this for 15+ years and have written quite a bit about it. We also had good luck with the overall uptake as there are over 2600 packages using Rcpp at CRAN alone, and you can search them all (!!) (plus actually any ones that once were on CRAN and are no longer) within the GitHub 'cran' organization: https://github.com/cran/
I also don't quite have the time and bandwidth now to discuss R internals here -- it is not the best place. (The r-devel and r-package-devel mailing lists may be better.) But I don't want to send you away: I was just looking to unique_ptr
in the context of Rcpp::XPtr
(and think it is 'nope') but there is possibly work to be done. (And I do work with zero copy memory outside of R right now for a work project.) But R is tricky, and it "takes some getting used to" and it is NOT the same as plain C++. Keep at it please, I think you will find it useful.
PS But maybe let go of Rcpp Modules. They have limitations. Look at what we do with Rcpp::Xptr
; look at R itself does with external pointers. I think there may be something to be done to revisit things with nesting / sharing but I haven't had time to get into it.
from rcpp.
I don't see anything wrong about how the Rcpp module manages this. The ref
method returns a const
reference, but as soon as we try to return it to R, we cannot ensure that property anymore, so it is copied here to return an external pointer. The copy however does not increment the counter, so the Test
class is missing a copy method which should do that:
Test(const Test& o){++count; Rcpp::Rcout<<"cstr " << count <<std::endl; }
from rcpp.
Actually, we could define a wrap_dispatch
without the const
specifier, and then return just the pointer to the reference. But then this would create two different S4 objects referencing the same pointer, and I don't know whether this could backfire.
And, in general, the issue is that Rcpp has no way of knowing whether a returned reference must be owned or it's already owned. So the sensible thing to do is to copy the object and set a finalizer on that copy, so that we ensure that it is garbage collected. These are the limitations, so IMO you should work with that in mind, and, in this case, define a copy constructor, or just don't export this method to R, and rely on copying the S4 object that wraps the C++ class.
from rcpp.
I'm not really good for understanding the R arcana, but I'll be very happy to help and improve how object are managed. I'll be very happy to have some information about.
- how the module magic works to expose the stuff declared in .method as callable in R? If I write some kind of wrapper for references or not owning pointer, it must be callable in R the same way the referenced or the pointed class is. I'm sorry but I was unable to find some documentation about it, so all help is welcomed.
- how do we define a SEXP pointing to a custome object?
- how do we define a custom destructor for a SEXP ?
I've also found some class called XPtr, that may be helpfull. I can wrap things to return a XPtr instead of a Test* but then, there is no method attached to it in R, so I cannot use it. I also don't know what XPtr does or how it manages its memory.
@Enchufa2.
Not all classes are copyable, here I don't want a Test(const Test&) function.
I may have added :
Test(const Test&)=delete;
As Test is accessed trough a reference, it's not supposed to be copied.
The fact R is garbage collected is not the main issue. The garbage collection may change WHEN the actual deletion occurs (so objects are not destroyed at the end of their scope, or when rm is called, but at some point in the future). A garbage collector actually breaks RAII, makes ressource management hell (typically when the code branches or throw exception) but it must not change HOW objects are deleted. Here we do have a double delete error, because delete is called on a not owning pointer (or on a reference), not because the actual deletion happens at an unexpected time.
from rcpp.
Here is the implementation of the previous idea.
The problem is that making it works requires a TON of glue code to actually expose objects for R.
If anyone can help me to find a way to write such code without repeating myself all the time, I'll greatly appreciate it.
The main idea is to wrap references in a class that actualy delete nothing, and that can be called in R with the same convension as the referenced class, so the R user doesn't see a difference when using it.
//--- test.cpp ---
#include <memory>
struct Test{
static int count;
Test(){++count; Rcpp::Rcout<<"cstr " << count <<std::endl; }
~Test(){--count; Rcpp::Rcout<<"dest " << count <<std::endl; }
Test(const Test&)=delete;
Test & ref(){return *this;}
//other methods
void f(){Rcpp::Rcout << "f"<<std::endl; }
};
int Test::count = 0;
struct Test_ref: std::reference_wrapper<Test> {
typedef std::reference_wrapper<Test> ptr_type;
using ptr_type::ptr_type;
};
#include <Rcpp.h>
using namespace Rcpp;
RCPP_EXPOSED_CLASS(Test);
RCPP_EXPOSED_CLASS(Test_ref);
Test_ref test_ref_w(Test*t){
return Test_ref( t->ref() ) ;
}
Test_ref Test_ref_ref_w(Test_ref*t){
return Test_ref(t->get().ref() ) ;
}
void Test_ref_f_w(Test_ref*t){
t->get().f() ;
}
RCPP_MODULE(test) {
class_<Test>("Test")
.constructor()
.method("ref", test_ref_w)
.method("f", &Test::f )
;
class_<Test_ref>("Test_ref")
.constructor<Test&>()
.method("ref", Test_ref_ref_w )
.method("f", Test_ref_f_w )
;
}
#--- test.R ---
library(Rcpp);
sourceCpp("test.cpp");
a = new(Test); # cstr 1, as expected
b=a$ref(); # prints nothing, as expected
a; #C++ object <0x55be83c6f070> of class 'Test' <0x55be8460d400> : OK A is of class Test
b; # C++ object <0x55be861df3e0> of class 'Test_ref' <0x55be84c87c10> : OK B is of class Test_ref, and has a different adress
#b and a can be used with the same interface, so the user doesn't care if it's a ref or not
a$f();
b$f();
rm(b);gc(); #print nothing : OK as b is destroyed WITHOUT touching a
rm(a);gc() # dest 0 : OK as we want to delete a
from rcpp.
I'm a little bit lost, but thank a lot for your help !
from rcpp.
That is par for the course. There is a lot here but you cannot do extensions modules for R / reason about how R should do things without knowing a little about R. We all started with the (somewhat hard to digest, but comprehensive) Writing R Extension (plus maybe the sibbling 'Language' and 'Internals' manuals). Many good nuggets are in Section 5 and below.
from rcpp.
Thank a lot !
I'll try to follow your advice. I still have things to learn, but it's getting clearer with the link you've posted.
from rcpp.
Also: "Simple works." I keep forgetting where I did this but I do have a package (or more) where I casually construct a C++ class as I like, use it as a singleton (hey, we R know is not multithread) accessible from a static pointer and then use Rcpp for helpers functions to
- allocate / init / setup
- setter and getter as I need it
- wind it down
(though I should add that by now I would probably use an XPtr instead .... That design is from younger days. Then again "simple works", and quite well at times.)
The R 'foreign function interface' is robust and time tested. There are things it does, and things is doesn't. But it is the only interface we have so we play the hand we've been dealt, not the one we dream we had.
from rcpp.
Related Issues (20)
- Avoid readability-avoid-const-params-in-decls? HOT 5
- Add support for check.names HOT 5
- Compiler warning on windows-arm64 (libc++) HOT 3
- Figure 3 incorrect caption in Rcpp-introduction vignette HOT 2
- Upstreaming `std::tuple<>` support HOT 10
- sourceCpp crashes R when called about 1000 times on same code HOT 10
- Integer overflow in `MatrixRow` offset calculations for large matrices HOT 8
- Switch from S4SXP to OBJSXP HOT 2
- pass by const refererence HOT 1
- A fresh -Wformat-security issue under r-devel HOT 9
- 'CRAN package Rcpp' notice HOT 5
- Installation/Compiling Error for Package with Rcpp dependency - Ubuntu (rvmf.cpp?) HOT 5
- Exporting classes using `Rcpp::interfaces(cpp)` that are shared among multiple cpp files through header file HOT 13
- test_packageversion fails with Rcpp 1.0.12 HOT 3
- Question regarding replacement by reference of <RTYPE>Array class without making copy HOT 8
- Cannot make std::vector<int8_t> from NumericVector HOT 4
- issue building phangorn 2.11 for wasm HOT 3
- New warnings from -Wconversion -Wno-sign-conversion HOT 3
- compileAttributes and use of .External2() for calling native symbol routine HOT 6
- Exporting comments into function definitions HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rcpp.