在函数中获取浏览状态

Moo*_*per 12 debugging r

我有一个这样的功能:

fun <- function() {
  browser()
  is_browsing()
} 
Run Code Online (Sandbox Code Playgroud)

我想知道代码is_browsing()应该是什么TRUE,以便在当前正在浏览函数时返回,因此控制台将如下所示:

> fun()
Called from: fun()
Browse[1]> 
debug at #3: is_browsing()
Browse[2]> 
TRUE
Run Code Online (Sandbox Code Playgroud)

但是,如果我注释掉该browser()行,或按 停止浏览cis_browsing()则应返回FALSE,如下所示:

> fun()
Called from: fun()
Browse[1]> c
FALSE
Run Code Online (Sandbox Code Playgroud)

我已经阅读了debuggingState()isdebugged()但它们似乎对我的情况没有多大帮助。

真实情况仅供参考,是在我们浏览时更新绘图或视图,但仅当我们正在浏览时,如果我们不是,我只想最终绘制/查看一次,以节省资源。

All*_*ron 7

从 Romain 代码中的想法开始,然后复制整个 RCNTXT 结构(加上它内部使用的几个其他结构),我设法让 C++ 代码返回R_GlobalContext.

C++ 代码如下所示:

#include <Rcpp.h>
#include <Rinternals.h>
#include <setjmp.h>

extern void* R_GlobalContext ;

typedef struct {int tag, flags; union {int ival; double dval; SEXP sxpval;} u;
} R_bcstack_t;

typedef struct{jmp_buf jmpbuf; int mask_was_saved, saved_mask;} sigjmp_buf[1];

typedef struct RCNTXT {
    struct RCNTXT *nextcontext;
    int callflag;
    sigjmp_buf cjmpbuf;
    int cstacktop, evaldepth;
    SEXP promargs, callfun, sysparent, call, cloenv, conexit;
    void (*cend)(void *);
    void *cenddata;
    void *vmax;
    int intsusp, gcenabled, bcintactive;
    SEXP bcbody;
    void* bcpc;
    SEXP handlerstack, restartstack;
    struct RPRSTACK *prstack;
    R_bcstack_t *nodestack;
    R_bcstack_t *bcprottop;
    SEXP srcref;
    int browserfinish;
    SEXP returnValue;
    struct RCNTXT *jumptarget;
    int jumpmask;
} RCNTXT, *context;

// [[Rcpp::export]]
Rcpp::List get_RCNTXT(int level){
  RCNTXT* res = (RCNTXT*)R_GlobalContext;
  if (level > 1) res = res->nextcontext;
  return Rcpp::List::create(Rcpp::Named("call_flag") = res->callflag,
                            Rcpp::Named("c_stack_top") = res->cstacktop,
                            Rcpp::Named("call_depth") = res->evaldepth,
                            Rcpp::Named("call_fun") = res->callfun,
                            Rcpp::Named("sys_parent") = res->sysparent,
                            Rcpp::Named("call") = res->call,
                            Rcpp::Named("cloenv") = res->cloenv,
                            Rcpp::Named("conexit") = res->conexit,
                            Rcpp::Named("promargs") = res->promargs,
                            Rcpp::Named("intsusp") = res->intsusp,
                            Rcpp::Named("gcenabled") = res->gcenabled,
                            Rcpp::Named("bcintactive") = res->bcintactive,
                            Rcpp::Named("handlerstack") = res->handlerstack,
                            Rcpp::Named("restartstack") = res->restartstack,
                            Rcpp::Named("srcref") = res->srcref,
                            Rcpp::Named("browserfinish") = res->browserfinish);
}
Run Code Online (Sandbox Code Playgroud)

这使我们可以查看以下内容R_Globalcontext

get_RCNTXT(1)
#> $call_flag
#> [1] 12
#> 
#> $c_stack_top
#> [1] 4
#> 
#> $call_depth
#> [1] 1
#> 
#> $call_fun
#> function (level) 
#> .Call(<pointer: 0x0000000071282ff0>, level)
#> <bytecode: 0x00000174169448d0>
#> 
#> $sys_parent
#> <environment: R_GlobalEnv>
#> 
#> $call
#> get_RCNTXT(1)
#> 
#> $cloenv
#> <environment: 0x0000017416c52a08>
#> 
#> $conexit
#> NULL
#> 
#> $promargs
#> $promargs[[1]]
#> NULL
#> 
#> 
#> $intsusp
#> [1] 0
#> 
#> $gcenabled
#> [1] 1
#> 
#> $bcintactive
#> [1] 0
#> 
#> $handlerstack
#> NULL
#> 
#> $restartstack
#> NULL
#> 
#> $srcref
#> NULL
#> 
#> $browserfinish
#> [1] 0
Run Code Online (Sandbox Code Playgroud)

不幸的是,browserfinish无论是否调用,该字段都只返回 0 browser。但是,如果get_RCNTXTbrowser提示符调用该函数,则restartstack显示它已从browser. 这允许在获得 C++ 代码后定义以下 R 函数:

is_browser <- function()
{
  R <- get_RCNTXT(1)$restartstack
  if(is.null(R)) return(FALSE)
  class(R[[1]]) == "restart"
}
Run Code Online (Sandbox Code Playgroud)

这允许从命令提示符查询浏览器状态:

is_browser()
#> [1] FALSE

> browser()
#> Called from: top level 
Browse[1]> is_browser()
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

然而,这并不像看起来那么有用。首先,它与基 R 中的以下代码具有相同的效果:

is_browser()
#> [1] FALSE

> browser()
#> Called from: top level 
Browse[1]> is_browser()
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

其次,当browser从函数内部调用时,它运行的代码在它自己的上下文而不是browser上下文中进行评估,这意味着is_browser将返回 FALSE。的 C 代码browser(实际函数do_browser在 main.c 中调用)编写了一个新的上下文,该上下文在函数退出后被删除,并且该上下文在函数运行期间显然没有被任何其他结构指向,所以它是很难看出如何is_browser编写以允许访问此上下文。

因此,您似乎需要编写一个新的实现browser来允许浏览的上下文知道它正在被浏览,而我们真的不想去那里。

另一方面,浏览器上下文可以完全访问浏览的上下文,并且由于您的最终目标是仅在浏览器模式下才允许像绘图这样的条件代码运行,我认为最好的解决方案是使用浏览器本身来告诉正在浏览的浏览上下文。

例如,如果你这样做:

is_browser <- function() {
  !is.null(findRestart("browser"))
}
Run Code Online (Sandbox Code Playgroud)

您现在可以在浏览时选择有条件地运行受if(is_browser()).

那么如果你有fun这样的(browser()注释掉):

browser_on <- function() {
  options(I_am_browsing = TRUE)
}

browser_off <- function() {
  options(I_am_browsing = FALSE)
}

is_browser <- function() {
  b <- getOption("I_am_browsing")
  if(is.null(b)) FALSE else b
}
Run Code Online (Sandbox Code Playgroud)

你会得到:

fun <- function() {
  #browser()
  if(is_browser()) plot(1:10)
  if(!is_browser()) "I didn't plot anything"
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您fun()从浏览器内部运行,则会得到:

fun()
#> [1] "I didn't plot anything"
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

如果它仍然有效 browser在里面调用fun

fun <- function() {
  browser()
  if(is_browser()) plot(1:10)
  if(!is_browser()) "I didn't plot anything"
}

fun()
#> Called from: fun()
Browse[1]> browser_on()
Browse[1]> 
#> debug at #3: if (is_browser()) plot(1:10)
Browse[2]> 
#> debug at #3: plot(1:10)
Browse[2]> 
#> debug at #4: if (!is_browser()) "I didn't plot anything"
Browse[2]>
Run Code Online (Sandbox Code Playgroud)

这不是一个完美的解决方案,因为它在浏览器中运行时需要一个额外的命令,并且它通过options. 如果您browser从同一范围多次调用,则需要跟踪此情况。特别是,browser_off()如果您正在调用,则应在退出浏览器之前小心调用browser从全局环境,则。


Wal*_*ldi 5

当您使用浏览器时,提示会显示浏览级别:
\n浏览[1]、浏览[2]、...

\n
> browser()\nCalled from: top level \nBrowse[1]> browser()\nCalled from: top level \nBrowse[2]> \n
Run Code Online (Sandbox Code Playgroud)\n

此浏览级别的计算方式为main.C

\n
browselevel = countContexts(CTXT_BROWSER, 1);\n
Run Code Online (Sandbox Code Playgroud)\n

其中CTXT_BROWSER是 中定义的常量defn.h

\n
CTXT_BROWSER  = 16\n
Run Code Online (Sandbox Code Playgroud)\n

您可以使用此内部countContexts函数来获取is_browsing您正在寻找的信息:

\n

is_browsing.cpp

\n
#include <Rcpp.h>\n#include <R.h>\n#include <Rinternals.h>\nusing namespace Rcpp;\n\n\n// [[Rcpp::export]]\nint is_browsing() {\n  return Rf_countContexts(16,1);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

测试 :

\n
library(Rcpp)\nsourceCpp(\'is_browsing.cpp\')\ntest <- function() {\n  is_browsing()\n}\n\ntest()\n#> [1] 0\n\nbrowser()\n#> Called from: eval(expr, envir, enclos)\n\ntest()\n#> [1] 1\n
Run Code Online (Sandbox Code Playgroud)\n

由reprex 包(v0.3.0)于 2020-08-29 创建

\n

如果在函数内调用浏览器也可以工作:

\n
test2 <- function() {\n  browser()\n   is_browsing()\n }\ntest2()\nCalled from: test2()\nBrowse[1]> n\ndebug \xc3\xa0 #3 :is_browsing()\nBrowse[2]> n\n[1] 1\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想要 TRUE / FALSE 返回,Rcpp 代码将是:

\n
> browser()\nCalled from: top level \nBrowse[1]> browser()\nCalled from: top level \nBrowse[2]> \n
Run Code Online (Sandbox Code Playgroud)\n

  • 实际上@Waldi,现在测试了这个,我认为你已经得到了这个。我很惊讶从函数内部调用时会检测到浏览器上下文。这几天来一直让我抓狂——你的发现给我留下了深刻的印象。为了完整起见,我可能会建议您获取 Rcpp 代码以返回“LogicalVector” - 我已冒昧地将其附加到您的答案中,但请随意删除它。 (2认同)
  • 感谢@Allan 的编辑:布尔值准确地回答了“is_browsing”的问题,但我将浏览器级别保留为输出,因为它可能是有用的额外信息。 (2认同)