【问题标题】:How to work with Rcpp strings variables which could be NULL?如何使用可能为 NULL 的 Rcpp 字符串变量?
【发布时间】:2022-01-06 01:54:18
【问题描述】:

我正在编写一个 R 包 + Rcpp 代码来使用现有的 C++ 库。

在阅读完此处的教程后:https://gallery.rcpp.org/articles/optional-null-function-arguments/,我正在为如何使用 NULL 和字符串而苦苦挣扎。我很困惑,我无法从类型 Rcpp::Nullable<std::string> 转换为 std::string (或等效地从 Rcpp::Nullable<Rcpp::String> 转换为 Rcpp::String

在 C++ 中,我正在检查字符串(在 C++ 中)是否为空。如果字符串为空,我想返回 NULL。如果字符串(在 C++ 中)不为空,我想返回字符串。

我的示例代码如下,为简单起见,修改了Rcpp.package.skeleton()提供的函数rcpp_hello_world()。我的目标是在 R 中返回一个包含字符串或 NULL(如果字符串为空)的列表 (Rcpp::List)。

#include <Rcpp.h>
#include <string>
using namespace Rcpp;

// [[Rcpp::export]]
Rcpp::List rcpp_hello_world() {

    // After calculations from external C++ library,
    // the variable 'mystring' will either empty (i.e. "") or populated (e.g. "helloworld")

    std::string mystring = "helloworld";  // string non-empty

    Rcpp::Nullable<std::string> result_string = R_NilValue;
    
    if (!mystring.empty()) {
        std::string result_string(mystring);
    }

    Rcpp::List z = List::create(result_string);

    return z ;
}

以上示例中的结果变量result_string 应该是NULL"mystring"---但是,以上将始终返回NULL,这不是所需的行为。

然后我尝试看看是否可以在Rcpp::Nullable&lt;std::string&gt;std::string 之间转换类型:

std::string mystring = "helloworld";  
Rcpp::Nullable<std::string> result_string = R_NilValue;
std::string result_string(mystring);

这会导致编译错误:

error: redefinition of 'result_string' with a different type: 'std::string' 
(aka 'basic_string<char, char_traits<char>, allocator<char>>') vs 'Rcpp::Nullable<std::string>' 
(aka 'Nullable<basic_string<char, char_traits<char>, allocator<char>>>')

我是否为此操作使用了错误的数据结构?或者如果值可以为 NULL,是否有更好的方法来处理字符串?

【问题讨论】:

  • 在调用 C++ 代码之前进行所有输入验证,包括检查是否为空
  • @HongOoi 我没有检查 NULL。我正在检查字符串是否为空。如果字符串为空,我想返回 NULL。如果字符串不为空,我想返回字符串。
  • @HongOoi 请让我知道这是否有意义---我无法在 R 中进行此检查;一切都在 C++ 中
  • 在调用 C++ 代码之前进行所有输入验证,包括检查空字符串
  • 错误信息很清楚。在您的 if 语句中,您有一个 std::string 类型 result_string 。在 if 语句(范围)之外,std::string 对象被破坏,然后您只有Rcpp 字符串,因此结果为 NULL。有了这个,我想你现在可以解决原始问题的后半部分了。

标签: c++ r rcpp stdstring r-package


【解决方案1】:

这是一个最低限度的完整答案。在函数体中你可以根据需要调整你的测试,这只是一个占位符的例子。

代码

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::List foo(Rcpp::NumericVector v) {

    // we just us a random vector here to determine: if positive
    // we inject a string, if negative NULL

    const std::string mystring = "helloworld";  // if positive

    int n = v.size();
    Rcpp::List z(n);

    for (int i=0; i<n; i++) {
        if (v[i] < 0) {
            z[i] = mystring;
        } else {
            z[i] = R_NilValue;
        }
    }

    return z;
}

/*** R
set.seed(123)
foo(rnorm(3)))
set.seed(123456)
foo(rnorm(3))
*/

输出

> Rcpp::sourceCpp("~/git/stackoverflow/70601602/answer.cpp")

> set.seed(123)

> foo(rnorm(3))
[[1]]
[1] "helloworld"

[[2]]
[1] "helloworld"

[[3]]
NULL


> set.seed(123456)

> foo(rnorm(3))
[[1]]
NULL

[[2]]
[1] "helloworld"

[[3]]
[1] "helloworld"

> 

【讨论】:

  • 感谢您的帮助!你是完全正确的——我对如何使用 Rcpp::Nullable 感到困惑。如前所述,“辅助类 Nullable 仅用于函数签名”。谢谢你让我直截了当!
  • 通常,一旦您仔细考虑,这一切都是有道理的——SEX{ .Call("functionname", SEXP a, SEXP b, ....) 提供的 C 语言外部函数接口需要Nullable&lt;&gt; 的体操,但我们不需要它(或使用它) ) 其他地方。
【解决方案2】:

Dirk 在上面是正确的:基本上,我的误解与对 Rcpp::Nullable 的混淆有关。

辅助类 Nullable 仅用于函数签名

以下内容最终为我工作:

Rcpp::List rcpp_hello_world() {

    // After calculations from external C++ library,
    // the variable 'mystring' will either empty (i.e. "") or populated (e.g. "helloworld")

    std::string mystring = "helloworld";  // string non-empty


    Rcpp::List z = Rcpp::List::create(Rcpp::Named("myresult") = nullptr);

    if (mystring.empty()) {
        z["myresult"] = R_NilValue;
    } else  {
        z["myresult"] = mystring;
    }
 
    return z ;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    相关资源
    最近更新 更多