[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