|
21 | 21 | namespace Rcpp{ |
22 | 22 | namespace internal{ |
23 | 23 |
|
| 24 | + // the address of the C++ object held by the module object whose |
| 25 | + // environment is env (see cpp_object_initializer() in R/Module.R) |
| 26 | + inline void* module_object_address(const Environment& env){ |
| 27 | + SEXP xp = env.get(".pointer") ; |
| 28 | + if( xp == R_NilValue ) |
| 29 | + throw not_compatible( "Expecting a C++ module object" ) ; |
| 30 | + |
| 31 | + // the dummy object of a class without a default constructor |
| 32 | + if( TYPEOF(xp) != EXTPTRSXP ) |
| 33 | + throw not_initialized() ; |
| 34 | + |
| 35 | + // e.g. a deserialized object |
| 36 | + void* ptr = R_ExternalPtrAddr(xp) ; |
| 37 | + if( ptr == NULL ) |
| 38 | + throw ::Rcpp::exception( "external pointer is not valid" ) ; |
| 39 | + |
| 40 | + return ptr ; |
| 41 | + } |
| 42 | + |
| 43 | +#ifndef RCPP_NO_MODULES |
| 44 | + // whether the module class cl is the C++ class typeinfo_name, or derives |
| 45 | + // from it through class_::derives(), which records each parent by its R |
| 46 | + // class name, "Rcpp_" followed by its name in the module |
| 47 | + inline bool module_class_is( class_Base* cl, Module* module, const char* typeinfo_name ){ |
| 48 | + if( cl->has_typeinfo_name(typeinfo_name) ) |
| 49 | + return true ; |
| 50 | + |
| 51 | + std::vector<class_Base*> todo(1, cl) ; |
| 52 | + std::set<class_Base*> seen ; |
| 53 | + while( module && !todo.empty() ){ |
| 54 | + class_Base* current = todo.back() ; |
| 55 | + todo.pop_back() ; |
| 56 | + if( !seen.insert(current).second ) |
| 57 | + continue ; |
| 58 | + |
| 59 | + if( current->has_typeinfo_name(typeinfo_name) ) |
| 60 | + return true ; |
| 61 | + |
| 62 | + for( size_t i = 0; i < current->parents.size(); i++ ){ |
| 63 | + const std::string& parent = current->parents[i] ; |
| 64 | + if( parent.compare(0, 5, "Rcpp_") != 0 ) |
| 65 | + continue ; |
| 66 | + std::string name = parent.substr(5) ; |
| 67 | + if( module->has_class(name) ) |
| 68 | + todo.push_back( module->get_class_pointer(name) ) ; |
| 69 | + } |
| 70 | + } |
| 71 | + return false ; |
| 72 | + } |
| 73 | +#endif |
| 74 | + |
| 75 | + // packages call this one directly, so it stays; it cannot check the class |
24 | 76 | inline void* as_module_object_internal(SEXP obj){ |
| 77 | + if( !Rf_isS4(obj) ) |
| 78 | + throw not_compatible( "Expecting a C++ module object" ) ; |
25 | 79 | Environment env(obj) ; |
26 | | - SEXP xp = env.get(".pointer") ; |
27 | | - return R_ExternalPtrAddr(xp ); |
| 80 | + return module_object_address(env) ; |
| 81 | + } |
| 82 | + |
| 83 | + inline void* as_module_object_internal(SEXP obj, const char* typeinfo_name){ |
| 84 | + if( !Rf_isS4(obj) ){ |
| 85 | + throw not_compatible( |
| 86 | + "Expecting an object of C++ class '%s'", |
| 87 | + demangle(typeinfo_name) |
| 88 | + ) ; |
| 89 | + } |
| 90 | + Environment env(obj) ; |
| 91 | + void* ptr = module_object_address(env) ; |
| 92 | + |
| 93 | +#ifndef RCPP_NO_MODULES |
| 94 | + SEXP cppclass = env.get(".cppclass") ; |
| 95 | + if( TYPEOF(cppclass) != EXTPTRSXP || R_ExternalPtrAddr(cppclass) == NULL ){ |
| 96 | + throw not_compatible( |
| 97 | + "Expecting an object of C++ class '%s'", |
| 98 | + demangle(typeinfo_name) |
| 99 | + ) ; |
| 100 | + } |
| 101 | + class_Base* cl = reinterpret_cast<class_Base*>( R_ExternalPtrAddr(cppclass) ) ; |
| 102 | + |
| 103 | + SEXP module_xp = env.get(".module") ; |
| 104 | + Module* module = NULL ; |
| 105 | + if( TYPEOF(module_xp) == EXTPTRSXP ) |
| 106 | + module = reinterpret_cast<Module*>( R_ExternalPtrAddr(module_xp) ) ; |
| 107 | + |
| 108 | + if( !module_class_is(cl, module, typeinfo_name) ){ |
| 109 | + throw not_compatible( |
| 110 | + "Expecting an object of C++ class '%s', got an object of class '%s'", |
| 111 | + demangle(typeinfo_name), |
| 112 | + cl->name |
| 113 | + ) ; |
| 114 | + } |
| 115 | +#endif |
| 116 | + |
| 117 | + return ptr ; |
28 | 118 | } |
29 | 119 |
|
30 | 120 | } |
|
0 commit comments