[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