简单的代码文件test2.cpp是
double tau;
// [[Rcpp::export]]
inline void set_tau(double t) {
tau = t;
}
// [[Rcpp::export]]
inline double get_tau() {
return tau;
}
它不会用 Rcpp::sourceCpp("./src/test2.cpp") 编译,但删除 inline 关键字后代码可以编译并运行:代码文件 test3.cpp 是
double tau;
// [[Rcpp::export]]
void set_tau(double t) {
tau = t;
}
// [[Rcpp::export]]
double get_tau() {
return tau;
}
Rcpp::sourceCpp("./src/test2.cpp") 抛出错误
test2.cpp: In function ‘SEXPREC* sourceCpp_1_set_tau(SEXP)’:
test2.cpp:27:44: error: invalid use of void expression
27 | rcpp_result_gen = Rcpp::wrap(set_tau(t));
| ^
make: *** [/usr/lib/R/etc/Makeconf:177: test2.o] Error 1
Error in Rcpp::sourceCpp("./src/test2.cpp") :
Error 1 occurred building shared library.
>
但是代码可以从命令行 g++ test2.cpp -g -c -o test2.o 正常编译,其中 g++ 是版本 9.4。另外 test3.cpp 编译并运行良好
> Rcpp::sourceCpp("./src/test3.cpp")
> set_tau(20)
> get_tau()
[1] 20
>
看来 Rcpp 版本 1.0.11 无法处理内联 void 函数。他们有解决方法吗?例如,g++ -o3 会生成一个没有 inline 关键字的内联函数吗?
您面临的问题可能是由于
inline
关键字在 C++ 和 R 中的处理方式不同。在 C++ 中,inline
是要求编译器考虑内联函数的请求,但它并不保证该函数将被内联。然而,在 R 中,inline
是保留字,不能用作函数名。
当您使用
Rcpp::sourceCpp()
时,R 解析器首先处理 C++ 代码,然后再将其传递给 C++ 编译器。 R 解析器看到 inline
关键字并将其视为无效函数名称,从而导致您遇到的错误。
对于您的解决方法问题,您可以考虑以下几种选择:
删除
inline
关键字:正如您已经发现的那样,从 C++ 代码中删除 inline
关键字可以解决该问题,并且代码可以通过 Rcpp::sourceCpp()
正确编译和运行。这是最简单的解决方案,但这意味着编译器将根据自己的启发来决定是否内联函数。
使用
__attribute__((always_inline))
而不是 inline
:这是一项 C++11 功能,明确告诉编译器始终内联函数,无论其大小或其他因素如何。但是,请记住,过多的内联会增加代码大小,并在某些情况下可能降低性能。
启用链接时优化 (LTO):如果您使用的是最新版本的 GCC 或 Clang,则可以通过将
-flto
标志传递给编译器来启用链接时优化 (LTO)。 LTO 执行整个程序优化,包括跨翻译单元的潜在内联函数。但是,启用 LTO 会增加编译时间和二进制大小。
使用
__forceinline
关键字(仅限 MSVC):如果您使用的是 Microsoft Visual C++ (MSVC),则可以使用 __forceinline
关键字而不是 inline
来显式请求函数内联。但是,这是一个非标准扩展,无法与其他编译器一起使用。
一般来说,建议避免过度使用
inline
,除非您有特定的性能原因这样做,并且已经仔细测量和分析了您的代码。现代编译器通常比人类更擅长决定内联哪些函数,不必要的内联会增加代码大小并可能降低性能。