gpt4 book ai didi

r - 当遇到异常情况时,R 扩展可以安全地分配内存吗?

转载 作者:行者123 更新时间:2023-12-03 07:44:38 28 4
gpt4 key购买 nike

我即将在 C++ 中为 R 编写一个扩展包,并且想知道如何使用动态内存管理而不会有内存泄漏的风险。我读过了

  • http://cran.r-project.org/doc/manuals/R-exts.html#Memory-allocation
  • http://cran.r-project.org/doc/manuals/R-exts.html#Garbage-Collection

  • 并立即提出三个问题:
  • 在出现 R 异常的情况下,R 是否会优雅地展开 C++ 堆栈帧,例如当R_alloc内存不足或 Rf_error由于其他情况而被调用? – 否则,我该怎么清理R_alloc 'ed 和 PROTECT编辑或干脆Calloc的内存?例如,将
    #include<R.h>
    // […]
    void someMethod () {
    char *buffer1 = NULL;
    char *buffer2 = NULL;
    try {
    ClassA a;
    buffer1 = R_Calloc( 10000, char );
    buffer2 = R_Calloc( 10000, char );
    // […]
    } finally {
    try {
    if ( NULL != buffer1 ) {
    R_Free( buffer1 );
    }
    } finally {
    if ( NULL != buffer2 ) {
    R_Free( buffer2 );
    }
    }
    }
    }

    保证调用析构函数~ClassA对于 aR_Free对于 buffer1buffer2 ?如果不是,那么保证这一点的 R 教科书方法是什么?
  • 可以标准 C++(现在已弃用)std::auto_ptr或现代 std::unique_ptr被用来简化内存分配的习语?
  • 是否有经过验证的 C++ 习惯用法/最佳实践在 C++ 标准模板库中使用 R 的内存分配,例如一些合适的分配器模板,以便 STL 类从 R 堆分配内存?
  • 最佳答案

    由于 Rf_error 确实会跳过 C++ 堆栈帧并因此绕过析构函数调用,我发现有必要进行更多的文档研究。特别是查看 RODBC 包和实验监控内存使用以确认调查结果,让我得出:

    1:立即将指针存储在 R 外部指针中并为此注册终结器。

    下面这个有点简单的例子说明了这个成语:

    #define STRICT_R_HEADERS    true

    #include <string>
    #include <R.h>
    #include <Rinternals.h> // defines SEXP

    using namespace std;

    class A {
    string name;
    public:
    A ( const char * const name ) : name( name ) { Rprintf( "Construct %s\n", name ); }
    ~A () { Rprintf( "Destruct %s\n", name.c_str() ); }
    const char* whoami () const { return name.c_str(); }
    };

    extern "C" {
    void finaliseAhandle ( SEXP handle ) {
    A* pointer = static_cast<A*>( R_ExternalPtrAddr( handle ) );
    if ( NULL != pointer ) {
    pointer->~A();
    R_Free( pointer );
    R_ClearExternalPtr( handle );
    }
    }

    SEXP createAhandle ( const SEXP name ) {
    A* pointer = R_Calloc( 1, A );
    SEXP handle = PROTECT( R_MakeExternalPtr(
    pointer,
    R_NilValue, // for this simple example no use of tag and prot
    R_NilValue
    ) );
    try {
    new(pointer) A( CHAR( STRING_ELT( name, 0 ) ) );
    R_RegisterCFinalizerEx( handle, finaliseAhandle, TRUE );
    } catch (...) {
    R_Free( pointer );
    R_ClearExternalPtr( handle );
    Rf_error( "construction of A(\"%s\") failed", CHAR( STRING_ELT( name, 0 ) ) );
    }
    // … more code may follow here, including calls to Rf_error.
    UNPROTECT(1);
    return handle;
    }

    SEXP nameAhandle ( const SEXP handle ) {
    A* pointer = static_cast<A*>( R_ExternalPtrAddr( handle ) );
    if( NULL != pointer ) {
    return mkChar( pointer->whoami() );
    }
    return R_NilValue;
    }

    SEXP destroyAhandle ( const SEXP handle ) {
    if( NULL != R_ExternalPtrAddr( handle ) ) {
    finaliseAhandle( handle );
    }
    return R_NilValue;
    }
    }
    NULL的分配指向 R_ClearExternalPtr( handle );中的指针防止重复调用 R_Free(pointer);`。

    请注意,建议的习惯用法仍然需要一些假设才能安全工作:如果构造函数不能在 R 的意义上失败,即通过调用 Rf_error .如果这无法避免,我的建议是将构造函数调用推迟到终结器注册之后,以便终结器在任何情况下都能够 R_Free内存。但是,为了不调用析构函数 ~A,必须包含逻辑。除非 A对象已被有效构造。在简单的情况下,例如当 A仅包含原始字段,这可能不是问题,但在更复杂的情况下,我建议包装 A变成 struct然后可以记住 A是否构造函数成功完成,然后为该结构分配内存。当然,我们还是要靠 A构造函数正常失败,释放它分配的所有内存,无论这是否由 C_alloc 完成或 malloc之类的。 (实验表明 R_alloc 中的内存在 Rf_error 的情况下会自动释放。)

    2:号码

    这两个类都与注册 R 外部指针终结器无关。

    3:是的。

    据我所见,将 C++ 和 R 的统治完全分开被认为是最佳实践。Rcpp 鼓励使用包装器( https://stat.ethz.ch/pipermail/r-devel/2010-May/057387.htmlcxxfunctionhttp://dirk.eddelbuettel.com/code/rcpp.html 中),这样 C++ 异常就不会影响 R引擎。

    在我看来,分配器可以被编程为使用 R_CallocR_Free .然而,为了抵消潜在的影响 Rf_error在此类调用期间,分配器将需要一些垃圾收集接口(interface)。我想在本地将分配器绑定(bind)到 PROTECTSEXP类型 externalptr其中有一个由 R_RegisterCFinalizerEx 注册的终结者并指向一个本地内存管理器,它可以在 Rf_error 的情况下释放内存.

    关于r - 当遇到异常情况时,R 扩展可以安全地分配内存吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30968436/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com