Skip to content

Instantly share code, notes, and snippets.

@Thell
Last active August 29, 2015 13:56
Show Gist options
  • Save Thell/9286048 to your computer and use it in GitHub Desktop.
Save Thell/9286048 to your computer and use it in GitHub Desktop.
/***
*
* Scenario:
*
* You desire to use RCPP_EXPOSE_CLASS on a class and it needs to store
* a pointer to somewhere in a constructor initialized container either
* using the default initializer list or from within the constructor.
*
* Problem:
*
* There is a behavior difference between the copy constructor behavior of
* the RCPP:: containers (IntegerVector) and the std:: containers where-by
* the rcpp versions re-use memory and the std:: versions are moved.
*
* Reproduction:
*
* Two classes, identical except for the underlying storage container types.
* - ClassA using IntegerVector.
* - ClassB using std::vector< int >.
*
*
* - Rcpp::sourceCpp('rcpp_issue.cpp')
*
* - Note that both the IntegerVector and std::vector classes have the copy
* constructor called when used via R. My guess is that because it is already
* in R protected/managed memory space it is re-usable... but honesly I know
* nothing about that.
*
*
* Fix for end-users :: provide a copy constructor.
* Fix for rcpp :: ??
*
*/
// [[Rcpp::plugins("cpp11")]]
#include <ostream>
#include <iterator>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <string>
#include <memory>
#include <RcppCommon.h>
class ClassA;
RCPP_EXPOSED_CLASS(ClassA)
class ClassB;
RCPP_EXPOSED_CLASS(ClassB)
#include <Rcpp.h>
using namespace Rcpp;
void print_ptrs(const int * p_vec, std::type_index vec_info,
const int * p_iter, std::type_index iter_info )
{
std::unordered_map< std::type_index, std::string > type_names;
type_names[std::type_index(typeid(IntegerVector))] = "IntegerVector";
type_names[std::type_index(typeid(IntegerVector::iterator))] =
"IntegerVector::iterator";
type_names[std::type_index(typeid(std::vector<int>))] = "std::vector";
type_names[std::type_index(typeid(std::vector<int>::iterator))] =
"std::vector iterator";
std::cout << " vec.begin() addr: " << p_vec << " ["
<< type_names[std::type_index(vec_info)] << "]" << std::endl
<< " iter addr: " << p_iter << " ["
<< type_names[std::type_index(iter_info)] << "]" << std::endl;
}
class ClassA {
public:
IntegerVector vec;
IntegerVector::iterator iter;
ClassA(int len) : vec(len), iter( vec.begin() )
{
std::cout << "Ctor ::" << std::endl;
std::cout << " vec type: " << typeid(vec).name() << std::endl
<< " iter type: " << typeid(iter).name() << std::endl
<< std::endl;
print_ptrs( std::addressof(*vec.begin()), std::type_index(typeid(vec)),
std::addressof(*iter), std::type_index(typeid(iter)) );
}
ClassA( const ClassA& other ) : vec(other.vec), iter(other.iter) {
std::cout << "Copy Ctor ::" << std::endl;
std::cout << "--- other ---" << std::endl;
print_ptrs( std::addressof(*other.vec.begin()), std::type_index(typeid(other.vec)),
std::addressof(*other.iter), std::type_index(typeid(other.iter)) );
std::cout << "--- this ---" << std::endl;
print_ptrs( std::addressof(*vec.begin()), std::type_index(typeid(vec)),
std::addressof(*iter), std::type_index(typeid(iter)) );
}
~ClassA() {
std::cout << "Dtor" << std::endl;
}
}; // end class: ClassA
class ClassB {
public:
std::vector< int > vec;
std::vector< int >::iterator iter;
ClassB(int len) : vec(len), iter( vec.begin() )
{
std::cout << "Ctor ::" << std::endl;
std::cout << " vec type: " << typeid(vec).name() << std::endl
<< " iter type: " << typeid(iter).name() << std::endl
<< std::endl;
print_ptrs( std::addressof(*vec.begin()), std::type_index(typeid(vec)),
std::addressof(*iter), std::type_index(typeid(iter)) );
}
ClassB( const ClassB& other ) : vec(other.vec), iter(other.iter) {
std::cout << "Copy Ctor ::" << std::endl;
std::cout << "--- other ---" << std::endl;
print_ptrs( std::addressof(*other.vec.begin()), std::type_index(typeid(other.vec)),
std::addressof(*other.iter), std::type_index(typeid(other.iter)) );
std::cout << "--- this ---" << std::endl;
print_ptrs( std::addressof(*vec.begin()), std::type_index(typeid(vec)),
std::addressof(*iter), std::type_index(typeid(iter)) );
}
~ClassB() {
std::cout << "Dtor" << std::endl;
}
}; // end class: ClassB
ClassA make_ClassA( int n ) { return ClassA(n); }
RCPP_MODULE(ClassA){
class_< ClassA >("ClassA")
.constructor< int >()
;
function( "make_ClassA", &make_ClassA );
}
ClassB make_ClassB( int n ) { return ClassB(n); }
RCPP_MODULE(ClassB){
class_< ClassB >("ClassB")
.constructor< int >()
;
function( "make_ClassB", &make_ClassB );
}
// [[Rcpp::export]]
void rcpp_behaviorA(int n)
{
auto bi = ClassA(n);
}
// [[Rcpp::export]]
void rcpp_behaviorB(int n)
{
auto bi = ClassB(n);
}
/*** R
############ RCPP #################
## ClassA and ClassB behave the same
# ClassA behavior in Rcpp function.
rcpp_behaviorA(1)
# ClassB behavior in Rcpp function.
rcpp_behaviorB(1)
############# R ###################
## ClassB gets a change of address.
bi <- make_ClassA(1)
rm(bi)
rm(ClassA)
# Exposed ClassB behavior in R.
bi <- make_ClassB(1)
rm(bi)
rm(ClassB)
gc() # Dtor
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment