2016-12-18 10 views
1

R 확장을 작성하는 데 사용하는 타사 C 라이브러리가 있습니다. 나는 라이브러리에 정의 된 몇 가지 구조체를 만들어서 S4 객체의 일부로 유지해야 할 필요가있다. (이 구조체를 계산 상태로 정의하는 것으로 생각하고 파괴하기 위해 나머지 모든 객체를 파괴해야한다. 계산 및 이미 계산 된 모든 결과). 포인터를 이러한 구조체를 void* 포인터로 유지하는 S4 개체를 만드는 것이 생각 중입니다. 그렇지만 그렇게하는 방법은 분명하지 않습니다. 슬롯의 형식은 무엇입니까? 당신이 저장해야 할 이유는 어떤 이유가 표시되지 않지만S4 구조체에 대한 포인터가있는 객체

+1

https://stat.ethz.ch/R-manual/R-devel/library/methods/html/BasicClasses.html :'externalptr' – hrbrmstr

답변

5

바와 같이 @hrbrmstr 지적, 당신은, 쓰기 R 확장의 this section에에 감동하는 "살아"등의 개체를 유지하기 위해 externalptr 유형을 사용할 수 있습니다 뭐든지 void*. C++을 조금 사용하는 데 문제가 없다면 Rcpp class XPtrEXTPTRSXP을 관리하는 데 필요한 상당한 양의 상용구를 제거 할 수 있습니다. 예를 들어, 다음과 같은 간단한 예는 당신의 타사 라이브러리의 API를 나타냅니다 가정

#include <Rcpp.h> 
#include <stdlib.h> 

typedef struct { 
    unsigned int count; 
    double total; 
} CStruct; 

CStruct* init_CStruct() { 
    return (CStruct*)::malloc(sizeof(CStruct)); 
} 

void free_CStruct(CStruct* ptr) { 
    ::free(ptr); 
    ::printf("free_CStruct called.\n"); 
} 

typedef Rcpp::XPtr<CStruct, Rcpp::PreserveStorage, free_CStruct> xptr_t; 

default finalizer 단순히 열린 개체에 대한 delete를 호출하기 때문에, Rcpp::XPtr<SomeClass>를 사용하는 것이 일반적으로 충분하다 new 통해 생성 된 포인터 작업. 그러나 C API를 다루므로, XPtrmalloc을 통해 할당 된 메모리에 delete을 호출하지 않도록 (기본값) 템플릿 매개 변수 Rcpp::PreserveStorage 및 더 중요한 것은 적합한 종료 자 (이 예에서는 free_CStruct)를 제공해야합니다. 해당 R 객체가 가비지 수집 될 때 등. 예를 계속

, 당신은 당신의 CStruct와 상호 작용하는 다음과 같은 기능을 쓰기 가정

    :

    // [[Rcpp::export]] 
    xptr_t MakeCStruct() { 
        CStruct* ptr = init_CStruct(); 
        ptr->count = 0; 
        ptr->total = 0; 
    
        return xptr_t(ptr, true); 
    } 
    
    // [[Rcpp::export]] 
    void UpdateCStruct(xptr_t ptr, SEXP x) { 
        if (TYPEOF(x) == REALSXP) { 
         R_xlen_t i = 0, sz = XLENGTH(x); 
         for (; i < sz; i++) { 
          if (!ISNA(REAL(x)[i])) { 
           ptr->count++; 
           ptr->total += REAL(x)[i]; 
          } 
         } 
         return; 
        } 
    
        if (TYPEOF(x) == INTSXP) { 
         R_xlen_t i = 0, sz = XLENGTH(x); 
         for (; i < sz; i++) { 
          if (!ISNA(INTEGER(x)[i])) { 
           ptr->count++; 
           ptr->total += INTEGER(x)[i]; 
          } 
         } 
         return; 
        } 
    
        Rf_warning("Invalid SEXPTYPE.\n"); 
    } 
    
    // [[Rcpp::export]] 
    void SummarizeCStruct(xptr_t ptr) { 
        ::printf(
         "count: %d\ntotal: %f\naverage: %f\n", 
         ptr->count, ptr->total, 
         ptr->count > 0 ? ptr->total/ptr->count : 0 
        ); 
    } 
    
    // [[Rcpp::export]] 
    int GetCStructCount(xptr_t ptr) { 
        return ptr->count; 
    } 
    
    // [[Rcpp::export]] 
    double GetCStructTotal(xptr_t ptr) { 
        return ptr->total; 
    } 
    
    // [[Rcpp::export]] 
    void ResetCStruct(xptr_t ptr) { 
        ptr->count = 0; 
        ptr->total = 0.0; 
    } 
    
    이 시점에서

    , 당신은 R에서 CStructs 처리를 시작하는 데 충분했을

  • ptr <- MakeCStruct()CStruct을 초기화하고 externalptr으로 저장합니다.
  • UpdateCStruct(ptr, x)CStruct에 저장된 데이터를 수정합니다, SummarizeCStruct(ptr)이 요약을 인쇄합니다 등
  • rm(ptr); gc() 따라서 free_CStruct(ptr)를 호출하고 사물의 C 측의 오브젝트를 파괴의 ptr 개체를 제거하고 실행하는 가비지 콜렉터를 강제로 또한

이러한 기능을 모두 한 곳에서 사용하기위한 하나의 옵션 인 S4 클래스 사용에 대해 언급했습니다. 그런 다음

setClass(
    "CStruct", 
    slots = c(
     ptr = "externalptr", 
     update = "function", 
     summarize = "function", 
     get_count = "function", 
     get_total = "function", 
     reset = "function" 
    ) 
) 

setMethod(
    "initialize", 
    "CStruct", 
    function(.Object) { 
     [email protected] <- MakeCStruct() 
     [email protected] <- function(x) { 
      UpdateCStruct([email protected], x) 
     } 
     [email protected] <- function() { 
      SummarizeCStruct([email protected]) 
     } 
     [email protected]_count <- function() { 
      GetCStructCount([email protected]) 
     } 
     [email protected]_total <- function() { 
      GetCStructTotal([email protected]) 
     } 
     [email protected] <- function() { 
      ResetCStruct([email protected]) 
     } 
     .Object 
    } 
) 

, 우리는이 같은 CStruct들과 함께 작업 할 수 있습니다 :

ptr <- new("CStruct") 
[email protected]() 
# count: 0 
# total: 0.000000 
# average: 0.000000 

set.seed(123) 
[email protected](rnorm(100)) 
[email protected]() 
# count: 100 
# total: 9.040591 
# average: 0.090406 

[email protected](rnorm(100)) 
[email protected]() 
# count: 200 
# total: -1.714089 
# average: -0.008570 

[email protected]() 
[email protected]() 
# count: 0 
# total: 0.000000 
# average: 0.000000 

rm(ptr); gc() 
# free_CStruct called. 
#   used (Mb) gc trigger (Mb) max used (Mb) 
# Ncells 484713 25.9  940480 50.3 601634 32.2 
# Vcells 934299 7.2 1650153 12.6 1308457 10.0 
물론

, 다른 옵션이있는 더 많거나 적은, Rcpp Modules을 사용하는 것입니다 여기에 하나의 가능성이다 R 측면의 클래스 정의 상용구를 처리하십시오 (단, S4 클래스보다는 참조 클래스 사용).

+3

우수 답변. 아마도 Rcpp Gallery 게시물 일 수도 있습니까? –