Skip to content

Commit fe8e3b1

Browse files
authored
Merge pull request #1139 from Enchufa2/fix/Rostreams
Address #928: enable global Rcout/Rcerr via RCPP_USE_GLOBAL_ROSTREAM
2 parents 98173e5 + 6892c70 commit fe8e3b1

File tree

7 files changed

+178
-4
lines changed

7 files changed

+178
-4
lines changed

ChangeLog

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
2021-01-28 Iñaki Ucar <iucar@fedoraproject.org>
2+
3+
* inst/include/Rcpp/iostream/Rstreambuf.h: Support for global Rcout/Rcerr by
4+
adding `-DRCPP_USE_GLOBAL_ROSTREAM` to `PKG_CPPFLAGS`. This behavior mimics
5+
std::cout/cerr. Required initialization code is automatically generated.
6+
* inst/include/Rcpp/routines.h: Ditto
7+
* src/api.cpp: Ditto
8+
* src/attributes.cpp: Ditto
9+
* src/rcpp_init.cpp: Ditto
10+
111
2021-01-21 Dirk Eddelbuettel <edd@debian.org>
212

313
* DESCRIPTION (Version, Date): Roll minor version

inst/include/Rcpp/iostream/Rstreambuf.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
//
33
// Rstreambuf.h: Rcpp R/C++ interface class library -- stream buffer
44
//
5-
// Copyright (C) 2011 - 2017 Dirk Eddelbuettel, Romain Francois and Jelmer Ypma
5+
// Copyright (C) 2011 - 2020 Dirk Eddelbuettel, Romain Francois and Jelmer Ypma
6+
// Copyright (C) 2021 Dirk Eddelbuettel, Romain Francois, Jelmer Ypma and Iñaki Ucar
67
//
78
// This file is part of Rcpp.
89
//
@@ -80,9 +81,14 @@ namespace Rcpp {
8081
::R_FlushConsole();
8182
return 0;
8283
} // #nocov end
83-
static Rostream<true> Rcout;
84-
static Rostream<false> Rcerr;
8584

85+
#ifdef RCPP_USE_GLOBAL_ROSTREAM
86+
extern Rostream<true>& Rcout;
87+
extern Rostream<false>& Rcerr;
88+
#else
89+
static Rostream<true> Rcout;
90+
static Rostream<false> Rcerr;
91+
#endif
8692

8793
}
8894

inst/include/Rcpp/routines.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#ifndef RCPP_ROUTINE_H
2424
#define RCPP_ROUTINE_H
2525

26+
#include <Rcpp/iostream/Rstreambuf.h>
27+
2628
#if defined(COMPILING_RCPP)
2729

2830
// the idea is that this file should be generated automatically by Rcpp::register
@@ -45,6 +47,9 @@ namespace Rcpp{
4547
void Rcpp_precious_teardown();
4648
SEXP Rcpp_precious_preserve(SEXP object);
4749
void Rcpp_precious_remove(SEXP token);
50+
51+
Rostream<true>& Rcpp_cout_get();
52+
Rostream<false>& Rcpp_cerr_get();
4853
}
4954

5055
SEXP rcpp_get_stack_trace();
@@ -155,6 +160,17 @@ namespace Rcpp {
155160
fun(token);
156161
}
157162

163+
inline attribute_hidden Rostream<true>& Rcpp_cout_get() {
164+
typedef Rostream<true>& (*Fun)();
165+
static Fun fun = GET_CALLABLE("Rcpp_cout_get");
166+
return fun();
167+
}
168+
inline attribute_hidden Rostream<false>& Rcpp_cerr_get() {
169+
typedef Rostream<false>& (*Fun)();
170+
static Fun fun = GET_CALLABLE("Rcpp_cerr_get");
171+
return fun();
172+
}
173+
158174
}
159175

160176
// The 'attribute_hidden' used here is a simple precessor defined from

inst/tinytest/test_global_rostream.R

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
2+
## Copyright (C) 2021 Iñaki Ucar
3+
##
4+
## This file is part of Rcpp.
5+
##
6+
## Rcpp is free software: you can redistribute it and/or modify it
7+
## under the terms of the GNU General Public License as published by
8+
## the Free Software Foundation, either version 2 of the License, or
9+
## (at your option) any later version.
10+
##
11+
## Rcpp is distributed in the hope that it will be useful, but
12+
## WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
## GNU General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
18+
19+
.runThisTest <- Sys.getenv("RunAllRcppTests") == "yes" && Sys.getenv("RunVerboseRcppTests") == "yes"
20+
21+
if (! .runThisTest) exit_file("Set 'RunVerboseRcppTests' and 'RunAllRcppTests' to 'yes' to run.")
22+
23+
library(Rcpp)
24+
25+
mkv <- "PKG_CPPFLAGS = -DRCPP_USE_GLOBAL_ROSTREAM"
26+
cfg <- "
27+
#ifndef RCPP_USE_GLOBAL_ROSTREAM
28+
#define RCPP_USE_GLOBAL_ROSTREAM
29+
#endif
30+
#include <Rcpp.h>
31+
using namespace Rcpp;"
32+
ptr <- "
33+
// [[Rcpp::export]]
34+
CharacterVector ptr%s() {
35+
CharacterVector out(2);
36+
std::ostringstream Rcout_address, Rcerr_address;
37+
Rcout_address << (void const *)(&Rcout);
38+
Rcerr_address << (void const *)(&Rcerr);
39+
out[0] = Rcout_address.str();
40+
out[1] = Rcerr_address.str();
41+
return out;
42+
}"
43+
alig <- "
44+
// [[Rcpp::export]]
45+
void toLeft() {
46+
Rcout << std::left;
47+
Rcerr << std::left;
48+
}
49+
// [[Rcpp::export]]
50+
void toRight() {
51+
Rcout << std::right;
52+
Rcerr << std::right;
53+
}"
54+
print <- '
55+
// [[Rcpp::export]]
56+
void something() {
57+
Rcout << std::setw(20) << "somethingRcout" << std::endl;
58+
Rcerr << std::setw(20) << "somethingRcerr" << std::endl;
59+
}'
60+
61+
# create package and write functions into separate translation units
62+
pkg_name <- "fooRostream"
63+
path <- tempdir()
64+
pkg_path <- file.path(path, pkg_name)
65+
src_path <- file.path(pkg_path, "src")
66+
67+
if (dir.exists(pkg_path)) unlink(pkg_path)
68+
Rcpp.package.skeleton(
69+
pkg_name, path=path, environment=environment(), example_code=FALSE)
70+
writeLines(c(cfg, sprintf(ptr, "A")), file.path(src_path, "ptrA.cpp"))
71+
writeLines(c(cfg, sprintf(ptr, "B")), file.path(src_path, "ptrB.cpp"))
72+
writeLines(c(cfg, alig), file.path(src_path, "alig.cpp"))
73+
writeLines(c(cfg, print), file.path(src_path, "print.cpp"))
74+
writeLines(mkv, file.path(src_path, "Makevars"))
75+
compileAttributes(pkg_path)
76+
77+
# tests
78+
testRostream <- function() {
79+
captureFun <- function(...) {
80+
err <- capture.output(
81+
out <- capture.output(..., type="output"), type="message")
82+
c(out, err)
83+
}
84+
res <- all(ptrA() == ptrB())
85+
res <- c(res, all(grepl("^ ", captureFun(something()))))
86+
toLeft() # change alignment globally
87+
res <- c(res, all(grepl("^s", captureFun(something()))))
88+
toRight() # restore
89+
res
90+
}
91+
92+
# test package
93+
lib_path <- file.path(path, "templib")
94+
dir.create(lib_path)
95+
install.packages(pkg_path, lib_path, repos=NULL, type="source")
96+
expect_true(require("fooRostream", lib.loc=lib_path, character.only=TRUE))
97+
expect_true(all(testRostream()))
98+
99+
# test sourceCpp
100+
sourceCpp(file.path(src_path, "ptrA.cpp"))
101+
sourceCpp(file.path(src_path, "ptrB.cpp"))
102+
sourceCpp(file.path(src_path, "alig.cpp"))
103+
sourceCpp(file.path(src_path, "print.cpp"))
104+
expect_true(all(testRostream()))
105+
106+
# cleanup
107+
on.exit(unlink(pkg_path, recursive=TRUE), add=TRUE)
108+
on.exit(unlink(lib_path, recursive=TRUE), add=TRUE)

src/api.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// api.cpp: Rcpp R/C++ interface class library -- Rcpp api
44
//
55
// Copyright (C) 2012 - 2020 Dirk Eddelbuettel and Romain Francois
6+
// Copyright (C) 2021 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
67
//
78
// This file is part of Rcpp.
89
//
@@ -20,6 +21,7 @@
2021
// along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
2122

2223
#define COMPILING_RCPP
24+
#define RCPP_USE_GLOBAL_ROSTREAM
2325

2426
#include <Rcpp.h>
2527

@@ -33,6 +35,18 @@ using namespace Rcpp;
3335
#endif
3436

3537
namespace Rcpp {
38+
// [[Rcpp::register]]
39+
Rostream<true>& Rcpp_cout_get() {
40+
static Rostream<true> Rcpp_cout;
41+
return Rcpp_cout;
42+
}
43+
// [[Rcpp::register]]
44+
Rostream<false>& Rcpp_cerr_get() {
45+
static Rostream<false> Rcpp_cerr;
46+
return Rcpp_cerr;
47+
}
48+
Rostream<true>& Rcout = Rcpp_cout_get();
49+
Rostream<false>& Rcerr = Rcpp_cerr_get();
3650

3751
namespace internal {
3852

src/attributes.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//
22
// attributes.cpp: Rcpp R/C++ interface class library -- Rcpp attributes
33
//
4-
// Copyright (C) 2012 - 2021 JJ Allaire, Dirk Eddelbuettel and Romain Francois
4+
// Copyright (C) 2012 - 2020 JJ Allaire, Dirk Eddelbuettel and Romain Francois
5+
// Copyright (C) 2021 JJ Allaire, Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
56
//
67
// This file is part of Rcpp.
78
//
@@ -795,6 +796,8 @@ namespace attributes {
795796

796797
std::string generateRArgList(const Function& function);
797798

799+
void initializeGlobals(std::ostream& ostr);
800+
798801
void generateCpp(std::ostream& ostr,
799802
const SourceFileAttributes& attributes,
800803
bool includePrototype,
@@ -2127,6 +2130,8 @@ namespace attributes {
21272130

21282131
// always bring in Rcpp
21292132
ostr << "using namespace Rcpp;" << std::endl << std::endl;
2133+
// initialize references to global Rostreams
2134+
initializeGlobals(ostr);
21302135

21312136
// commit with preamble
21322137
return ExportsGenerator::commit(ostr.str());
@@ -2742,6 +2747,16 @@ namespace attributes {
27422747
return argsOstr.str();
27432748
}
27442749

2750+
// Generate the C++ code required to initialize global objects
2751+
void initializeGlobals(std::ostream& ostr) {
2752+
ostr << "#ifdef RCPP_USE_GLOBAL_ROSTREAM" << std::endl;
2753+
ostr << "Rcpp::Rostream<true>& Rcpp::Rcout = Rcpp::Rcpp_cout_get();";
2754+
ostr << std::endl;
2755+
ostr << "Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();";
2756+
ostr << std::endl;
2757+
ostr << "#endif" << std::endl << std::endl;
2758+
}
2759+
27452760
// Generate the C++ code required to make [[Rcpp::export]] functions
27462761
// available as C symbols with SEXP parameters and return
27472762
void generateCpp(std::ostream& ostr,
@@ -3188,6 +3203,8 @@ namespace {
31883203
// always include Rcpp.h in case the user didn't
31893204
ostr << std::endl << std::endl;
31903205
ostr << "#include <Rcpp.h>" << std::endl;
3206+
// initialize references to global Rostreams
3207+
initializeGlobals(ostr);
31913208
generateCpp(ostr, sourceAttributes, true, false, contextId_);
31923209
generatedCpp_ = ostr.str();
31933210
std::ofstream cppOfs(generatedCppSourcePath().c_str(),

src/rcpp_init.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Rcpp_init.cpp : Rcpp R/C++ interface class library -- Initialize and register
33
//
44
// Copyright (C) 2010 - 2020 John Chambers, Dirk Eddelbuettel and Romain Francois
5+
// Copyright (C) 2021 John Chambers, Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
56
//
67
// This file is part of Rcpp.
78
//
@@ -124,6 +125,8 @@ void registerFunctions(){
124125
RCPP_REGISTER(Rcpp_precious_teardown)
125126
RCPP_REGISTER(Rcpp_precious_preserve)
126127
RCPP_REGISTER(Rcpp_precious_remove)
128+
RCPP_REGISTER(Rcpp_cout_get)
129+
RCPP_REGISTER(Rcpp_cerr_get)
127130
#undef RCPP_REGISTER
128131
}
129132

0 commit comments

Comments
 (0)