我发现我们可以从一个内存地址构建/重建一个外部指针,参见这个例子,我从一个数据表对象中获取一个指针并重建它:
# 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 魔法持开放态度。
我需要这个用于{constructive}包,说我想探索
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")
#> )
我在包中有其他方法来处理环境,例如从列表构建等效环境等......但我想集成一个仅使用内存地址的替代方案,因为它会打印得更好并且足以满足某些用例。
如果存在安全且便携的版本,您需要做更多的工作来使其安全且便携。请注意,整数类型
uintptr_t
和相应的宏格式说明符SCNxPTR
在C99中是可选的。请参阅 WRE 中“编写可移植包”下的建议。
/* objectFromAddress.c */
#include <stdio.h> /* sscanf */
#include <inttypes.h> /* SCNxPTR */
#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
的地址会怎样?