[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 17:34:22 CEST 2013


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 ... 


----- 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