R语言 我可以从内存地址构建环境对象吗?

yjghlzjz  于 2023-04-03  发布在  其他
关注(0)|答案(1)|浏览(112)

我发现我们可以从内存地址构建/重建外部指针,请参阅这个例子,我从数据表对象中获取指针并重建它:

# devtools::install_github("randy3k/xptr")
iris_dt <- data.table::as.data.table(iris)
ptr1 <- attr(iris_dt, ".internal.selfref")
ptr1
#> <pointer: 0x13c00d4e0>
typeof(ptr1)
#> [1] "externalptr"

address <- xptr::xptr_address(ptr1)
address
#> [1] "0x13c00d4e0"
ptr2 <- xptr::new_xptr(address)
identical(ptr1, ptr2)
#> [1] TRUE

显然xptr::new_xptr("0x13c00d4e0")在会话之间是不稳定的,我知道上面不是分配内存,而是定义绑定,这对我的用例来说很好。
我想对环境做同样的事情:

e <- new.env()
e
#> <environment: 0x10b5bf038>

env("0x10b5bf038") # I want this "env" function
#> <environment: 0x10b5bf038>

我怀疑base R可以做到这一点,所以我对打包选项和C魔法持开放态度。

不需要阅读X/Y注解

对于the {constructive} package,我需要这个,比如说我想探索asNamespace("stats")$.__NAMESPACE__.$DLLs对象,它打印的方式不是很有帮助:

asNamespace("stats")$.__NAMESPACE__.$DLLs
#> $stats
#> DLL name: stats
#> Filename: /opt/R/4.2.1-arm64/Resources/library/stats/libs/stats.so
#> Dynamic lookup: FALSE

dput()通常是丑陋和脆弱的,另外这里的代码不是语法的,所以我不能确定对象被准确地描述了。

dput(asNamespace("stats")$.__NAMESPACE__.$DLLs)
#> list(stats = structure(list(name = "stats", path = "/opt/R/4.2.1-arm64/Resources/library/stats/libs/stats.so", 
#>     dynamicLookup = FALSE, handle = <pointer: 0x2011ce960>, info = <pointer: 0x6000021f00c0>), class = "DLLInfo"))

str()稍好一些,但在一般情况下并不理想。

str(asNamespace("stats")$.__NAMESPACE__.$DLLs)
#> List of 1
#>  $ stats:List of 5
#>   ..$ name         : chr "stats"
#>   ..$ path         : chr "/opt/R/4.2.1-arm64/Resources/library/stats/libs/stats.so"
#>   ..$ dynamicLookup: logi FALSE
#>   ..$ handle       :Class 'DLLHandle' <externalptr> 
#>   ..$ info         :Class 'DLLInfoReference' <externalptr> 
#>   ..- attr(*, "class")= chr "DLLInfo"

{constructive}保证了它输出的代码可以重现对象,现在它对包含指针的对象起作用。

constructive::construct(asNamespace("stats")$.__NAMESPACE__.$DLLs)
#> list(
#>   stats = list(
#>     name = "stats",
#>     path = "/opt/R/4.2.1-arm64/Resources/library/stats/libs/stats.so",
#>     dynamicLookup = FALSE,
#>     handle = constructive::external_pointer("0x2051d6960") |>
#>       structure(class = "DLLHandle"),
#>     info = constructive::external_pointer("0x600002970de0") |>
#>       structure(class = "DLLInfoReference")
#>   ) |>
#>     structure(class = "DLLInfo")
#> )

我在包中有其他方法来处理环境,例如从列表中构建等效环境等......但我想集成一个仅使用内存地址的替代方案,因为它可以更好地打印,并且对于某些用例来说已经足够了。

ujv3wf0j

ujv3wf0j1#

如果存在安全和可移植的版本,你需要做更多的工作来使它安全和可移植。注意整数类型uintptr_t和对应的宏格式说明符SCNxPTRoptional in C99。请参阅WRE中的“编写可移植的包”下的建议。

/* objectFromAddress.c */

#include <inttypes.h> /* uintptr_t, SCNxPTR */
#include <stdio.h> /* sscanf */
#include <Rinternals.h> /* SEXP, etc. */

SEXP objectFromAddress(SEXP a) {
    uintptr_t p = 0;
    
    if (TYPEOF(a) != STRSXP || XLENGTH(a) != 1 ||
        (a = STRING_ELT(a, 0)) == NA_STRING ||
        sscanf(CHAR(a), "%" SCNxPTR, &p) != 1)
        error("'a' is not a formatted unsigned hexadecimal integer");
    
    return (SEXP) p;
}
tools::Rcmd(c("SHLIB", "objectFromAddress.c"))
using C compiler: ‘Apple clang version 13.0.0 (clang-1300.0.29.30)’
using SDK: ‘MacOSX12.1.sdk’
clang -I"/usr/local/lib/R/include" -DNDEBUG   -I/opt/R/arm64/include -I/usr/local/include    -fPIC  -Wall -g -O2 -pedantic -mmacosx-version-min=11.0 -arch arm64 -falign-functions=64 -Wno-error=implicit-function-declaration -flto=thin -c objectFromAddress.c -o objectFromAddress.o
clang -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -Wall -g -O2 -pedantic -mmacosx-version-min=11.0 -arch arm64 -falign-functions=64 -Wno-error=implicit-function-declaration -flto=thin -fPIC -Wl,-mllvm,-threads=4 -L/usr/local/lib/R/lib -L/opt/R/arm64/lib -L/usr/local/lib -o objectFromAddress.so objectFromAddress.o -L/usr/local/lib/R/lib -lR -Wl,-framework -Wl,CoreFoundation
dyn.load("objectFromAddress.so")
(e <- new.env())
<environment: 0x112811b30>
identical(.Call("objectFromAddress", "112811b30"), e)
[1] TRUE

你想问的问题:

  • 如果在调用objectFromAddress之前,垃圾回收器释放了该地址的内存,会发生什么情况?
  • 如果提供的地址不是SEXPREC的地址,会发生什么?

在实践中,您(维护者)需要从R* 中保证 * 在这两种情况下都不会调用此函数。

相关问题