İşaretçiyi bir Rcpp işlevinden "yeni" bir nesneye döndürmenin uygun yolu


9

1) potansiyel olarak büyük bir bellek baskısı olan özel bir sınıf ve 2) bazı ön işlem gerçekleştiren, daha sonra özel sınıfımızın yeni bir nesnesini oluşturan ve döndüren üst düzey bir işlev düşünün. Değere göre gereksiz kopyalamayı önlemek için işlev, nesneyi ayırır ve onun yerine bir işaretçi döndürür.

Önceki bir tartışmaya dayanarak, yeni oluşturulmuş bir nesneye bir işaretçiyi döndürmenin uygun yolunun onu kaydırmak olduğu anlaşılıyor Rcpp::XPtr<>. Bununla birlikte, R daha sonra etkili bir şekilde görür externalptrve onu modern RCPP_EXPOSED_CLASSve bir RCPP_MODULEşeyler yapmanın yolunu bulmak için uygun yolu bulmakta zorlanıyorum .

Alternatif ham işaretçiyi döndürmektir. Ama sonra nesne belleğinin düzgün bir şekilde temizlendiğinden% 100 emin değilim. valgrindBellek sızıntılarını test etmek için koştum ve hiçbir şey bulamadı. Ancak, temizlik kim yapar? R?

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 cinsinden

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

Benim sorum Rcpp::Xptr<>işaretçiler döndürmek için uygun bir yol olup olmadığını , ve eğer öyleyse, R, sonucu olarak Doubledeğil görmek için nasıl alabilirim externalptr? Alternatif olarak, ham işaretçiyi döndürmek bellek sorunlarına neden olmazsa, işlevin oluşturduğu nesneyi kim temizler?


Evet, muhtemelen Rcpp::XPtrC ++ kodundan harici bir işaretçi oluşturmak istiyorsunuz . Ve bunu yapmak istersiniz double *ya da yükünüz ne olursa olsun. Burada, Galeri'de, GitHub'da örnekler olmalı ... Belki de motive edici bir arama ile yeterince yakın bir şey bulabilirsiniz?
Dirk Eddelbuettel

Merhaba @DirkEddelbuettel Oyuncular gerçekten olmalı CustomClass*. Gerçek uygulama R eşdeğeri olmayan özel bir veri yapısıdır ve tüm etkileşimler RCPP_MODULE. Motive aramamın bulduğu en yakın eşleşme , 7 yıl önceki bir gönderiydi , burada bir template <> CustomClass* as()dönüştürücü tanımlamam gerekiyor gibi görünüyor . Bununla birlikte, nasıl etkileşime girmesi gerektiği konusunda net değilim RCPP_MODULEve RCPP_EXPOSED_CLASSözellikle ikincisinin zaten tanımlanmış olduğunu düşündüğümden wrap()ve as().
Artem Sokolov

Romain'in aynı konudaki gönderisi de çok yararlıdır, ancak maalesef, işaretçilerin ele alınması yerine doğrudan nesnelerin kullanımını vurgular.
Artem Sokolov

1
Benzer şeyler yaptığımı biliyorum ama şimdi bunun en iyi örneğinin ne olduğundan emin değilim. Bir 'singleton' nesnesini açıkça ayarlayabilir ve modül olarak sarabilirsiniz (RcppRedis); Sanırım bir ya da iki işte anlattığınız şeyi yaptım ama şimdi iyi bir kamu örneği düşünemiyorum. Sonra tekrar - çeşitli veritabanı sarmalayıcılar ve erişim paketi bunu yapın. En küçük konu değil, belki bir oyuncak / sahte uygulama ile başlayın ve oradan inşa edin?
Dirk Eddelbuettel

Kullanmak RCPP_EXPOSED_CLASSve RCPP_MODULEgerçekten bunu yapmanın yolu mu? Daha önce hiç kullanmadım veya görmedim.
F. Privé

Yanıtlar:


7

Farklı yaklaşımlara ayrı ayrı bakmak mantıklı geliyor. Bu, ayrımı daha net hale getirir. Bunun Rcpp Modülleri vinyetindeki tartışmaya oldukça benzer olduğunu unutmayın.

Kullanırken Rcpp::XPtrsınıfınız var ve ortaya çıkarmak istediğiniz her yöntem için dışa aktarılan C ++ işlevleri sağlayın:

#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)
*/

Çıktı:

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

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

> squareDouble(d2)
[1] 29.16

R'de nesnenin sadece bir "işaretçi" olduğuna dikkat edin. Daha güzel bir şey istiyorsanız R tarafına bir S4 / RC / R6 / ... sınıfı ekleyebilirsiniz.

Harici işaretçiyi R tarafındaki bir sınıfa sarmak, Rcpp modüllerini kullanarak ücretsiz olarak alacağınız bir şeydir:

#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()
*/

Çıktı:

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

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

> d1$square()
[1] 29.16

Ayrıca C ++ 'da yapıcı yerine fabrika yönteminin kullanılması, ancak R tarafında aynı kullanımın kullanılması da desteklenir:

#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()
*/

Çıktı:

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

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

> d1$square()
[1] 29.16

Son olarak, RCPP_EXPOSED_CLASSbir R tarafı fabrika işlevini Rcpp Modülleri ile birleştirmek istiyorsanız kullanışlı olur, çünkü bu, nesneleri R ve C ++ arasında ileri geri aktarmak için gereken Rcpp::asve Rcpp::wrapuzantıları oluşturur . Fabrika function, yaptığınız gibi veya daha doğal bulduğum Rcpp Özniteliklerini kullanarak ihraç edilebilir:

#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()
*/

Çıktı:

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

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

> d1$square()
[1] 29.16

Temizleme ile ilgili: Hem Rcpp::XPtrve Rcpp Modülleri, nesnenin yıkıcısını çağıran varsayılan bir sonlandırıcı kaydeder. Gerekirse özel bir sonlandırıcı da ekleyebilirsiniz.

Bu yaklaşımlardan biri için öneri vermekte zorlanıyorum. Belki de her birini basit bir örnek üzerinde denemek ve kullanmak için daha doğal bulduğunuzu görmek en iyisidir.


2
Çok güzel şeyler. Burada yuvarlanıyorsunuz.
Dirk Eddelbuettel

Teşekkür ederim. Bu son derece yararlı! Sanırım factoryeksik olduğum anahtar konektör parçası.
Artem Sokolov

Küçük bir takip olarak, functionbir finalizer kaydedip kaydetmediğini biliyor musunuz , yoksa sadece factory mı?
Artem Sokolov

1
@ArtemSokolov AFAIK, yıkıcıyı çağıran varsayılan sonlandırıcı tarafından oluşturulur class_<T>ve nesnenin nasıl oluşturulduğundan bağımsızdır.
Ralf Stubner
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.