[Rcpp-devel] Module with vector of a class with inheritance, how to avoid slicing

Krzysztof Sakrejda krzysztof.sakrejda at gmail.com
Thu Apr 4 18:45:35 CEST 2013


On Thu, Apr 4, 2013 at 12:10 PM, Robin Girard
<robin.girard at mines-paristech.fr> wrote:
> but when I do
>
> res=new("Rcpp_vector_Of_father",1:10)
> res[[1]]$WhoAmI()
> res[[0]]$WhoAmI()
>
> rm(res)
> gc()
>
> that crashes with gc()

Dear Robin,

I realize you did not ask for book suggestions, but a gentle walk
through all these C++ issues (Slicing, constructors/destructors, and
the joy of pointers) is contained in Koenig & Moo's "Accelerated C++:
practical programming by example."

Sincerely,

Krzysztof

> R.
>
>
>
> ----- Mail original -----
> De: "Robin Girard" <robin.girard at mines-paristech.fr>
> À: "Romain Francois" <romain at r-enthusiasts.com>
> Cc: rcpp-devel at lists.r-forge.r-project.org
> Envoyé: Jeudi 4 Avril 2013 18:08:50
> Objet: Re: [Rcpp-devel] Module with vector of a class with inheritance, how to avoid slicing
>
> 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
>
> _______________________________________________
> 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
> _______________________________________________
> 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



--

Krzysztof Sakrejda-Leavitt

Organismic and Evolutionary Biology
University of Massachusetts, Amherst
319 Morrill Science Center South
611 N. Pleasant Street
Amherst, MA 01003

work #: 413-325-6555
email: sakrejda at cns.umass.edu
-----------------------------------------------


More information about the Rcpp-devel mailing list