[Rcpp-devel] Module with vector of a class with inheritance, how to avoid slicing
Robin Girard
robin.girard at mines-paristech.fr
Thu Apr 4 18:08:50 CEST 2013
that works.... indeed :)
R.
----- Mail original -----
De: "Romain Francois" <romain at r-enthusiasts.com>
À: "Robin Girard" <robin.girard at mines-paristech.fr>
Cc: rcpp-devel at lists.r-forge.r-project.org
Envoyé: Jeudi 4 Avril 2013 17:58:42
Objet: Re: [Rcpp-devel] Module with vector of a class with inheritance, how to avoid slicing
Le 04/04/13 17:34, Robin Girard a écrit :
> Thanks a lot Romain ! you're amazingly fast and furious (got my seatbelt). I'll definitely buy the book.
>
> That works on my example, I confirm (I had a side problem with using _t which I removed but don't see where this comes from, my side for sure). Now I'm trying to implement it on my "real world" case and I fear that memory management with this solution might poses a small problems.
>
> Continuing with our example assume the children class has a field "value_" of type double
>
> class children1 : public father
> {
> public:
> double value_
> ~children1(){};
> children1(){};
> children1(double value) : value_(value){};
> children1(children1 const & x){};
> virtual void WhoAmI() const{
> Rcout<<"son1"<<endl;
> };
> };
>
> and one want to create a constructor (in the class vector_Of_father) for a huge vector of children with a NumericVector as input (one value of the vector for one children in the vector)
>
> this will require a loop in c++ and my first try could have been
>
> vector_Of_father(Rcpp::NumericVector vec) : MyfatherList_(vec.size())
> {
> Rcpp::NumericVector::iterator MyNumericVector_iterator=vec.begin();
> int i=0;
> while ( MyNumericVector_iterator!=vec.end())
> {
> children1 *tmp=Calloc(1,children1); //
> tmp->value_=*MyNumericVector_iterator;
> MyfatherList_[i]=tmp;
> ++MyNumericVector_iterator; i++;
> }
> };
> and I would felt guilty not doing that:
> ~vector_Of_father()
> {
> std::vector<father*>::iterator it=MyfatherList_.begin();
> while (it!=MyfatherList_.end()){
> Free(*it);
> ++it;
> }
> };
>
> but that would clearly be against the spirit of Rcpp, I know, and for sure I would not dare to do that (OK, I admit, I tryed and this gave me a leak, (all I deserve ?) )
>
> is there a possibility to convert this into something less intrusive in term of memory allocation ?
> I know that's not a big problem since I can do the (big) loop in R... but loops in R are like uses of Calloc with Rcpp no ? they are possible but if we can avoid them ...
I would just use new and delete, and likely combine this with some STL
ness to make nicer looking code. Something like this perhaps:
children* new_children1( double x){
return new children1( x ) ;
}
vector_Of_father(Rcpp::NumericVector vec) : MyfatherList_(vec.size()){
std::transform( vec.begin(), vec.end(), MyfatherList_.begin(),
new_children ) ;
}
Same for the destructor:
template<typename T>
void deleter( T* ptr ){ delete ptr; }
~vector_Of_father() {
std::for_each( MyfatherList_.begin(), MyfatherList_.end(),
deleter<father> ) ;
};
Of course, with C++11 you could use lambdas instead of the new_children1
and deleter functions, but that's another story.
Now, this design implies that the vector_Of_father class is repsonsible
for the memory of its pointers. That's fine. But you need to keep this
in mind for when you do stuff with the class, e.g. when you assign an
element to the vector, make sure you delete the previous one, etc ...
Romain
> ----- Mail original -----
> De: "Romain Francois" <romain at r-enthusiasts.com>
> À: rcpp-devel at lists.r-forge.r-project.org
> Cc: "Robin Girard" <robin.girard at mines-paristech.fr>
> Envoyé: Mercredi 3 Avril 2013 22:12:48
> Objet: Re: [Rcpp-devel] Module with vector of a class with inheritance, how to avoid slicing
>
> Hi,
>
> Fasten your seatbelts.
>
> First, the problem is definitely object slicing. The best resource I
> know about to explain it is a chapter of Scott Meyers "Effective C++".
> Robin, I would highly recommend that you get it. Specially considering
> that you are willing to get your hands dirty.
>
> Anyway, I would suggest that your MyfatherList_ is a vector of father
> pointers:
>
> std::vector<father*> MyfatherList_;
>
> if you use any kind of object, even smart pointers, you'll get slicing.
>
>
> Then, you need to have a virtual destructor on the father class and make
> the WhoAmI methods virtual. This is how you do inheritance in C++.
>
> class father
> {
> public:
> virtual ~father(){};
> father(){};
> father(father const & x) {};
> virtual void WhoAmI() const{
> Rcout<<"father"<<endl;
> };
> };
>
> class children1_t : public father
> {
>
> public:
> ~children1_t(){};
> children1_t(){};
> children1_t(children1_t const & x){};
> virtual void WhoAmI() const{
> Rcout<<"son1"<<endl;
> };
> };
>
> This is very basic C++ inheritance stuff.
>
>
>
> The vector class looks like this:
>
> class vector_Of_father {
>
> public:
> std::vector<father*> MyfatherList_;
> ~vector_Of_father(){};
> vector_Of_father() : MyfatherList_(){};
>
> father* vec_get( int i) { return MyfatherList_.at(i); };
>
> void WhoAmI(int i) const {
> MyfatherList_[i]->WhoAmI();
> };
> int size(){ return(MyfatherList_.size()); };
>
> void push_back(object<father> func){
> MyfatherList_.push_back(func);
> };
>
> };
>
> Some comments.
> - As I said above, you are storing a vector of pointers.
> - that makes the implementation of WhoAmI, size simple.
> - for get_vec, you can just return a pointer to a father. We support this.
> - for push_back, you need to use object<father>, that is just a
> disguised pointer. just using father* would require declaring how to
> handle it. We don't support this yet (for some definition of yet).
> Anyway, you can just see object<T> as T*
>
>
> Now the module code.
>
> RCPP_MODULE(mod_example2){
>
> class_<father>( "father" )
> .constructor()
> .method("WhoAmI",&father::WhoAmI)
> ;
>
> class_<children1_t>( "children1_t" )
> .derives<father>("father" )
> .constructor()
> ;
>
> class_<children2_t>( "children2_t" )
> .derives<father>("father" )
> .constructor()
> ;
>
> class_<vector_Of_father>( "vector_Of_father")
> .constructor()
> .method( "size", &vector_Of_father::size)
> .method( "WhoAmI",&vector_Of_father::WhoAmI )
> .method( "push_back", &vector_Of_father::push_back )
> .method("[[",&vector_Of_father::vec_get)
> ;
>
> }
>
> That's it. You don't need to redefine WhoAmI for children classes. The
> .derives makes it for you.
>
> And with this:
>
> res <- new( vector_Of_father )
> res$push_back(new( father ))
> res$push_back(new( children1_t ))
> res$push_back(new( children2_t ))
> res$WhoAmI(2)
> res$WhoAmI(1)
> res$WhoAmI(0)
> res[[2]]$WhoAmI()
> res[[1]]$WhoAmI()
> res[[0]]$WhoAmI()
>
> I do get:
>
> son2
> son1
> father
> son2
> son1
> father
>
>
>
>
> Here is the full code of the .cpp file:
>
> #include <Rcpp.h>
>
> using namespace std;
> using namespace Rcpp;
>
> class father ;
> class children1_t;
> class children2_t ;
> class vector_Of_father;
>
> RCPP_EXPOSED_CLASS(father)
> RCPP_EXPOSED_CLASS(children1_t)
> RCPP_EXPOSED_CLASS(children2_t)
> RCPP_EXPOSED_CLASS(vector_Of_father)
>
> class father
> {
> public:
> virtual ~father(){};
> father(){};
> father(father const & x) {};
> virtual void WhoAmI() const{
> Rcout<<"father"<<endl;
> };
> };
>
> class children1_t : public father
> {
>
> public:
> ~children1_t(){};
> children1_t(){};
> children1_t(children1_t const & x){};
> virtual void WhoAmI() const{
> Rcout<<"son1"<<endl;
> };
> };
>
> class children2_t : public father
> {
> public:
> ~children2_t(){};
> children2_t(){};
> children2_t(children2_t const & x){};
> virtual void WhoAmI() const{
> Rcout<<"son2"<<endl;
> };
> };
>
>
> class vector_Of_father {
>
> public:
> std::vector<father*> MyfatherList_;
> ~vector_Of_father(){};
> vector_Of_father() : MyfatherList_(){};
>
> father* vec_get( int i) { return MyfatherList_.at(i); };
>
> void WhoAmI(int i) const {
> MyfatherList_[i]->WhoAmI();
> };
> int size(){ return(MyfatherList_.size()); };
>
> void push_back(object<father> func){
> MyfatherList_.push_back(func);
> };
>
> };
>
>
> RCPP_MODULE(mod_example2){
>
> class_<father>( "father" )
> .constructor()
> .method("WhoAmI",&father::WhoAmI)
> ;
>
> class_<children1_t>( "children1_t" )
> .derives<father>("father" )
> .constructor()
> ;
>
> class_<children2_t>( "children2_t" )
> .derives<father>("father" )
> .constructor()
> ;
>
> class_<vector_Of_father>( "vector_Of_father")
> .constructor()
> .method( "size", &vector_Of_father::size)
> .method( "WhoAmI",&vector_Of_father::WhoAmI )
> .method( "push_back", &vector_Of_father::push_back )
> .method("[[",&vector_Of_father::vec_get)
> ;
>
> }
>
> Romain
>
>
> Le 03/04/13 18:43, Robin Girard a écrit :
>> I am still working on my vector of a class father with inheritance (polymorphic subclasses ?). I created an exemple below that works fine but I face the known problem of "slicing" as named here : http://stackoverflow.com/questions/10154977/c-vector-with-inheritance and fail to implement the proposed solution.
>>
>> I have 2 tried 2 things and ended up with 2 questions:
>>
>> Question 1 - I tryed the solution proposed in the link with make_shared and shared_ptr. I had to add PKG_CXXFLAGS=-g -std=c++0x since my MinGW compiler did not find any -std=c++11. I got no compilation error but when I run the code it slices my children class :
>>
>>> res=new("Rcpp_vector_Of_father")
>>> res$push_back(new("Rcpp_father"))
>>> res$push_back_children1_t(new("Rcpp_children1_t"))
>>> res$WhoAmI(1)
>> father
>>> res$WhoAmI(0)
>> father
>>>
>>> res[[1]]$WhoAmI()
>> father
>>> res[[0]]$WhoAmI()
>> father
>>
>>
>> Question 2 - I tryed a solution with XPtr (although I'm not sure how this works) and got the following error at the module compilation
>> example_mod_consumption.cpp: In member function 'void vector_Of_father::push_back_children1_t(children1_t)':
>> example_mod_consumption.cpp:80:53: error: no matching function for call to 'std::vector<Rcpp::XPtr<father> >::push_back(Rcpp::XPtr<children1_t>)'
>
> XPtr<father> has no relationship with XPtr<children1_t>. They are
> independant classes generated by the XPtr template class. They don't
> know about the inheritance.
>
>> below are the code for the two approach and at the end the code of the module.
>>
>>
>>
>> -------------------- Solution with XPtr
>>
>> #include <Rcpp.h>
>>
>> using namespace std;
>> using namespace Rcpp;
>>
>> class father ;
>> class children1_t;
>> class children2_t ;
>> class vector_Of_father;
>>
>> RCPP_EXPOSED_CLASS(father)
>> RCPP_EXPOSED_CLASS(children1_t)
>> RCPP_EXPOSED_CLASS(children2_t)
>> RCPP_EXPOSED_CLASS(vector_Of_father)
>>
>> class father
>> {
>> public:
>> ~father(){};
>> father(){};
>> father(father const & x) {};
>> void WhoAmI() const{
>> Rcout<<"father"<<endl;
>> };
>> };
>>
>> class children1_t : public father
>> {
>>
>> public:
>> ~children1_t(){};
>> children1_t(){};
>> children1_t(children1_t const & x){};
>> void WhoAmI() const{
>> Rcout<<"son1"<<endl;
>> };
>> };
>>
>> class children2_t : public father
>> {
>> public:
>> ~children2_t(){};
>> children2_t(){};
>> children2_t(children2_t const & x){};
>> void WhoAmI() const{
>> Rcout<<"son2"<<endl;
>> };
>> };
>>
>>
>> class vector_Of_father {
>>
>> public:
>> std::vector<XPtr<father> > MyfatherList_;
>> //std::vector<shared_ptr<father> > MyfatherList_;
>> ~vector_Of_father(){};
>> vector_Of_father() : MyfatherList_(){};
>> father vec_get( int i) { return(*(MyfatherList_.at(i))); };
>>
>> void WhoAmI(int i) const
>> {
>> MyfatherList_[i]->WhoAmI();
>> };
>> int size(){ return(MyfatherList_.size()); };
>>
>> void push_back(father func){
>> MyfatherList_.push_back(XPtr<father>(&func));
>> };
>>
>> void push_back_children1_t(children1_t func){
>> MyfatherList_.push_back(XPtr<children1_t>(&func));
>> };
>> };
>>
>> -------------------- Solution with make_shared and shared_ptr
>>
>> only the class vector_of_father is different also #include <memory> is added after Rcpp.h include.
>>
>> class vector_Of_father {
>>
>> public:
>> std::vector<shared_ptr<father> > MyfatherList_;
>> ~vector_Of_father(){};
>> vector_Of_father() : MyfatherList_(){};
>>
>> father vec_get( int i) { return(*(MyfatherList_.at(i))); };
>>
>> void WhoAmI(int i) const
>> {
>> MyfatherList_[i]->WhoAmI();
>> };
>> int size(){ return(MyfatherList_.size()); };
>>
>>
>> void push_back(father func){
>> MyfatherList_.push_back(make_shared<father>(func));
>> };
>> void push_back_children1_t(children1_t func){
>> MyfatherList_.push_back(make_shared<children1_t>(func));
>> };
>> };
>>
>>
>>
>> -------------- the module code
>>
>> RCPP_MODULE(mod_example2){
>> using namespace Rcpp;
>>
>> class_<father>( "father" )
>> //constructors
>> .constructor()
>> .method("WhoAmI",&father::WhoAmI)
>> ;
>>
>> class_<children1_t>( "children1_t" )
>> .derives<father>("father" )
>> //constructors
>> .constructor()
>> .method("WhoAmI",&children1_t::WhoAmI)
>> ;
>>
>> class_<children2_t>( "children2_t" )
>> .derives<father>("father" )
>> //constructors
>> .constructor()
>> .method("WhoAmI",&children2_t::WhoAmI)
>> ;
>>
>> class_<vector_Of_father>( "vector_Of_father")
>> .constructor()
>> // .constructor<int>()
>> .method( "size", &vector_Of_father::size)
>> // .method("capacity", &cplfunctionvec::capacity,"Return size of allocated storage capacity. Returns the size of the storage space currently allocated for the vector, expressed in terms of elements.")
>> // .method( "max_size", &cplfunctionvec::max_size)
>> .method( "WhoAmI",&vector_Of_father::WhoAmI )
>> //.method( "test",&vector_Of_father::test )
>> .method( "push_back", &vector_Of_father::push_back )
>> .method("push_back_children1_t",&vector_Of_father::push_back_children1_t)
>> // .const_method( "at", &cplfunctionvec::at )
>> .method("[[",&vector_Of_father::vec_get)
>> // .method("[[<-",&vector_Of_father::vec_set)
>> ;
>>
>> }
>>
>>
>> Dr. Girard Robin
>> Chargé de Recherche
>>
>> MINES-ParisTech / Département Energétique et Procédés / PERSEE / Groupe ERSEI
>> Centre Procédés, Energies Renouvelables et Systèmes Energétiques (PERSEE)
>> Center for Processes, Renewables Energies and Energy Systems
>> Renewable Energies & Smartgrids (ERSEI)
>>
>> 1 Rue Claude Daunesse - CS 10207 - F-06904 Sophia Antipolis Cedex
>> Tel: +33.4.93.67.89.64 (~99), Fax: +33.4.93.95.75.35
>> e-mail : robin.girard at mines-paristech.fr
>>
>> web page perso http://www.mines-paristech.fr/Services/Annuaire/&?id=8828
>> statoverflow : http://stats.stackexchange.com/users/223/robin-girard
>> web page centre PERSEE : http://www.cep.mines-paristech.fr/
>> linkedin : http://www.linkedin.com/profile/view?id=14907272&trk=tab_pro
>
>
--
Romain Francois
Professional R Enthusiast
+33(0) 6 28 91 30 30
R Graph Gallery: http://gallery.r-enthusiasts.com
blog: http://blog.r-enthusiasts.com
|- http://bit.ly/ZTFLDo : Simpler R help tooltips
`- http://bit.ly/YFsziW : R Help tooltips
More information about the Rcpp-devel
mailing list