Rcpp 함수에서`new` 객체에 대한 포인터를 반환하는 올바른 방법


9

1) 잠재적으로 큰 메모리 인쇄를 가진 사용자 정의 클래스와 2) 일부 전처리를 수행 한 다음 사용자 정의 클래스의 새 오브젝트를 작성하여 리턴하는 최상위 기능을 고려하십시오. 값으로 불필요한 복사를 피하기 위해 함수는 객체를 할당하고 대신 포인터를 반환합니다.

이전 토론을 바탕으로 새로 만든 객체에 대한 포인터를 반환하는 올바른 방법은로 래핑하는 것 같습니다 Rcpp::XPtr<>. 그러나 R은 그것을 효과적으로 것으로보고 externalptr있으며 현대 RCPP_EXPOSED_CLASS와 그것을 캐스팅하는 적절한 방법을 찾기 위해 고심 하고 있습니다.RCPP_MODULE 일하는 하고 있습니다.

대안은 raw 포인터를 반환하는 것입니다. 그러나 객체 메모리가 올바르게 정리되었는지 100 % 확신하지 못합니다. valgrind메모리 누수를 테스트하기 위해 실행 했지만 아무것도 찾지 못했습니다. 그러나 누가 정리합니까? 아르 자형?

test.cpp

#include <Rcpp.h>

// Custom class
class Double {
public:
  Double( double v ) : value(v) {}
  double square() {return value*value;}
private:
  double value;
};

// Make the class visible
RCPP_EXPOSED_CLASS(Double)

// Option 1: returning raw pointer
Double* makeDouble( double x ) {
  Double* pd = new Double(x);
  return pd;
}

// Option 2: returning XPtr<>
SEXP makeDouble2( double x ) {
  Double* pd = new Double(x);
  Rcpp::XPtr<Double> ptr(pd);
  return ptr;
}

RCPP_MODULE(double_cpp) {
  using namespace Rcpp;

  function( "makeDouble", &makeDouble );
  function( "makeDouble2", &makeDouble2 );

  class_<Double>("Double")
    .constructor<double>("Wraps a double")
    .method("square", &Double::square, "square of value")
    ;
}

R에서

Rcpp::sourceCpp("test.cpp")
d1 <- makeDouble(5.4)     # <-- who cleans this up???
# C++ object <0x56257d628e70> of class 'Double' <0x56257c69cf90>
d1$square()
# 29.16

d2 <- makeDouble2(2.3)
# <pointer: 0x56257d3c3cd0>
d2$square()
# Error in d2$square : object of type 'externalptr' is not subsettable

내 질문은 Rcpp::Xptr<>포인터를 반환하는 올바른 방법 인지 여부 이며, 그렇다면 R을 결과가 Double아닌 으로 보는 방법은 externalptr무엇입니까? 또는 원시 포인터를 반환해도 메모리 문제가 발생하지 않으면 누가 함수가 생성 한 객체를 정리합니까?


예, 아마도 Rcpp::XPtrC ++ 코드에서 외부 포인터를 만들고 싶을 것입니다 . 그리고 당신은 그것을 double *하거나 당신의 페이로드가 무엇이든 캐스팅하려고합니다 . 갤러리, GitHub에 여기에 예제가있을 것입니다 ... 동기 부여 된 검색으로 충분히 가까운 것을 찾을 수 있습니까?
Dirk Eddelbuettel

안녕하세요 @DirkEddelbuettel 캐스트는 정말로되어야합니다 CustomClass*. 실제 응용 프로그램은 R에 해당하지 않는 사용자 지정 데이터 구조이며 모든 상호 작용은에 의해 제공되는 기능을 통해 수행됩니다 RCPP_MODULE. 내가 찾은 가장 근접한 검색 결과는 7 년 전의 게시물 인데, 여기서 template <> CustomClass* as()변환기 를 정의해야합니다 . 그러나, 나는 그것과 상호 작용하는 방법에 대한 불분명 오전 RCPP_MODULERCPP_EXPOSED_CLASS나는 후자는 이미 정의 된 생각, 특히 이후 wrap()as().
Artem Sokolov

같은 스레드에서 얻은 Romain의 게시물도 매우 유용하지만 불행히도 포인터를 처리하는 대신 객체 사용을 직접 강조합니다.
Artem Sokolov

1
나는 비슷한 일을했지만 지금은 가장 좋은 예를 모르겠습니다. '단일'오브젝트를 명확하게 설정하고 모듈 (RcppRedis)로 랩핑 할 수 있습니다. 나는 당신이 이전 직장에서 2 ~ 2 번 묘사 한 것을했다고 생각하지만 지금은 좋은 예를 생각할 수 없습니다. 그런 다음 다시 다양한 데이터베이스 래퍼와 액세스 패키지가이를 수행합니다. 가장 작은 주제가 아니기 때문에 장난감 / 모의 구현으로 시작하여 거기에서 빌드 할 수 있습니까?
Dirk Eddelbuettel

를 사용 RCPP_EXPOSED_CLASS하고 RCPP_MODULE실제로하는 방법입니까? 나는 전에 그것을 사용하거나 본 적이 없다.
F. Privé

답변:


7

다른 접근법을 개별적으로 보는 것이 합리적이라고 생각합니다. 이것은 구별을 더 명확하게 만듭니다. 이것은 Rcpp Modules 비 네트에 대한 설명과 매우 유사합니다.

사용할 때 Rcpp::XPtr클래스가 있고 노출하려는 모든 메소드에 대해 내 보낸 C ++ 함수를 제공하십시오.

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

// [[Rcpp::export]]
Rcpp::XPtr<Double> makeDouble(double x) {
    Double* pd = new Double(x);
    Rcpp::XPtr<Double> ptr(pd);
    return ptr;
}

// [[Rcpp::export]]
double squareDouble(Rcpp::XPtr<Double> x) {
    return x.get()->square();
}

/***R
(d2 <- makeDouble(5.4))
squareDouble(d2)
*/

산출:

> Rcpp::sourceCpp('59384221/xptr.cpp')

> (d2 <- makeDouble(5.4))
<pointer: 0x560366699b50>

> squareDouble(d2)
[1] 29.16

R에서 객체는 "포인터"일뿐입니다. 더 좋은 것을 원한다면 R쪽에 S4 / RC / R6 / ... 클래스를 추가 할 수 있습니다.

Rc 클래스의 외부 포인터를 Rcpp 모듈을 사용하면 무료로 얻을 수 있습니다.

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .constructor<double>("Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

산출:

> Rcpp::sourceCpp('59384221/modules.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x560366452eb0> of class 'Double' <0x56036480f320>

> d1$square()
[1] 29.16

C ++에서 생성자 대신 팩토리 메소드를 사용하지만 R 측에서 동일한 사용법을 사용하는 것도 지원됩니다.

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

Double* makeDouble( double x ) {
    Double* pd = new Double(x);
    return pd;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .factory<double>(makeDouble, "Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

산출:

> Rcpp::sourceCpp('59384221/modules-factory.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x5603665aab80> of class 'Double' <0x5603666eaae0>

> d1$square()
[1] 29.16

마지막으로, RCPP_EXPOSED_CLASS이 생성 한 이후, Rcpp 모듈과 R 측 공장의 기능을 결합하려는 경우에 유용 Rcpp::as하고 Rcpp::wrap++ 통과하는 데 필요한 확장 개체 R과 C 사이의 등을 백업 할 수 있습니다. 공장은을 통해 수출 될 수있었습니다function 당신이했던 것처럼 또는 Rcpp 속성을 사용하여 .

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

// Make the class visible
RCPP_EXPOSED_CLASS(Double)

// [[Rcpp::export]]
Double makeDouble( double x ) {
    Double d(x);
    return d;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- makeDouble(5.4))
d1$square()
*/

산출:

> Rcpp::sourceCpp('59384221/modules-expose.cpp')

> (d1 <- makeDouble(5.4))
C++ object <0x560366ebee10> of class 'Double' <0x560363d5f440>

> d1$square()
[1] 29.16

정리 관련 : 둘 다 Rcpp::XPtr Rcpp 모듈과 Rcpp 모듈 객체의 소멸자를 호출하는 기본 종료자를 등록합니다. 필요한 경우 사용자 정의 종료자를 추가 할 수도 있습니다.

이러한 접근법 중 하나에 대한 권장 사항을 제시하기가 어렵습니다. 어쩌면 간단한 예제에서 각각을 시도하고 사용하기가 더 자연스러운 것을 보는 것이 가장 좋습니다.


2
아주 좋은 물건. 당신은 여기 롤에 있습니다.
Dirk Eddelbuettel

감사합니다. 이것은 매우 도움이됩니다! 내가 factory놓친 주요 커넥터 조각 이라고 생각 합니다.
Artem Sokolov

작은 후속 조치로, 종료자를 function등록 하는지 또는 아는 것 factory 입니까?
Artem Sokolov

1
@ArtemSokolov AFAIK 소멸자를 호출하는 기본 종료자는 생성되며 class_<T>개체 생성 방법과 무관합니다.
Ralf Stubner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.