对于我的优化,我想在 Rcpp 中获得一个不错的 toupper。我对 C++ 很陌生,至于知道我已经做到了:

#include <Rcpp.h>
using namespace Rcpp;

void C_toupper_String(const String& s) {
  for (char *p =(char *)s.get_cstring();*p!=0;p++) *p = toupper(*p);
}
// [[Rcpp::export]]
StringVector C_toupper(StringVector const& vecteur) {
  StringVector res=clone(vecteur);
  for (int i(0); i < res.size(); ++i) {
    C_toupper_String(res[i]);
  }
  return res;
}

/*** R
teststring <- "HeY I arNaud"
C_toupper(teststring)
toupper(teststring)
identical(C_toupper(teststring),toupper(teststring))
*/

但是,它不能正常工作。

> C_toupper(teststring)
[1] "HEY I ARNAUD"

> toupper(teststring)
[1] "HEY I ARNAUD"

> identical(C_toupper(teststring),toupper(teststring))
[1] FALSE

有什么问题?如果可能,我不想将 String 转换为 std::string ,因为我想了解发生了什么:进入 C++ 的重点是能够避免复制和转换。

谢谢,

阿诺

最佳答案

为什么这两个字符串不测试 identical 的问题很难解释——在检查它们的原始字节(通过 charToRaw )时,这两个字符串看起来肯定是一样的,它们不携带属性,也没有编码集。所以,实际上,它们应该是相同的。

为了解开这个谜团,我们需要了解你的 C++ 代码实际上在做什么。更具体地说,C_toupper_String 中的 C 风格类型转换正在做什么。由于他们的危险, you should never use C-style casts 。你的代码遇到问题纯粹是因为那个 Actor 。

为什么?因为 String::get_cstring 返回 char const* 。您正在将其转换为 char*,从而丢弃其 const ness。这可能是安全的,但前提是底层存储不是 const 。否则就是 undefined behaviour (UB) 。由于代码重写(例如优化),UB 的影响很难预测。在这种情况下,它似乎产生了混淆 R 字符串内部结构的代码。

您基本上不能就地修改 Rcpp::String 对象,他们不允许这样做。但是如果你只是想避免复制,那么你的代码无论如何都没有达到它的目标,因为你的 C_toupper 函数在第一步中显式地复制了输入。

正如 Dirk 所说,解决这个问题的正确方法是使用可用的 Rcpp API。在字符串修改的情况下,这意味着将您的输入转换为 std::string ,执行修改,然后转换回来。这确实复制,但您当前的代码也是如此。这是编写此代码的一种好方法:

#include <Rcpp.h>
#include <cctype>
#include <string>

// [[Rcpp::export]]
Rcpp::StringVector C_toupper(Rcpp::StringVector const& vec) {
    std::vector<std::string> res(vec.begin(), vec.end());
    for (std::string& str : res) {
        for (char& c : str) c = std::toupper(c);
    }

    return Rcpp::wrap(res);
}

请注意,这有时会产生错误的结果,因为 std::toupper 根本无法处理某些 Unicode 特性。 R 的 toupper 做得更好,但也有一些问题。正确的解决方案是使用 {stringr} 或 {stringi} 包。

关于c++ - 自制的 : looks the same but not identical,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59860617/

10-12 20:39