Skip to content

Commit 0c955db

Browse files
authored
Add RcppThread::Rcerr (#60)
* add RcppThread::Rcerr * update README.md
1 parent 6b23af5 commit 0c955db

File tree

6 files changed

+99
-4
lines changed

6 files changed

+99
-4
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Provides R-friendly threading functionality:
1010

1111
* thread safe versions of [Rcpp's](http://www.rcpp.org/)
12-
`checkUserInterrupt()` and `Rcout`,
12+
`checkUserInterrupt()`, `Rcout`, and `Rcerr`,
1313
* an interruptible thread class that otherwise behaves like
1414
[`std::thread`](http://en.cppreference.com/w/cpp/thread/thread),
1515
* classes for the [thread pool
@@ -29,6 +29,8 @@ or the [API documentation](https://tnagler.github.io/RcppThread/).
2929

3030
Since then, the following **new features** have been added:
3131

32+
- Printing to the error stream with `Rcerr`.
33+
3234
- Free-standing functions like `parallelFor()` now dispatch
3335
to a global thread pool that persists for the entire session. This
3436
significantly speeds up programs that repeatedly call these functions.
@@ -99,13 +101,14 @@ before including any headers in your source code.
99101
1. Add the line `CXX_STD = CXX11` to the `src/Makevars(.win)` files of your package.
100102
2. Add `RcppThread` to the `LinkingTo` field of your `DESCRIPTION` file.
101103

102-
## Automatic override of `std::cout` and `std::thread`
104+
## Automatic override of `std::cout`, `std::cerr``, and `std::thread`
103105

104-
There are preprocessor options to replace all occurrences of `std::cout` and
105-
`std::thread` with calls to `RcppThread::Rcout` and `RcppThread::Thread`
106+
There are preprocessor options to replace all occurrences of `std::cout`, `std::cerr`, and `std::thread` with calls to `RcppThread::Rcout`, `RcppThread::Rcerr`, and `RcppThread::Thread`
106107
(provided that the RcppThread headers are included first). To enable this, use
108+
107109
```
108110
#define RCPPTHREAD_OVERRIDE_COUT 1 // std::cout override
111+
#define RCPPTHREAD_OVERRIDE_CERR 1 // std::cerr override
109112
#define RCPPTHREAD_OVERRIDE_THREAD 1 // std::thread override
110113
```
111114
before including the RcppThread headers.

inst/include/RcppThread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "RcppThread/RMonitor.hpp"
1010
#include "RcppThread/Rcout.hpp"
11+
#include "RcppThread/Rcerr.hpp"
1112
#include "RcppThread/Thread.hpp"
1213
#include "RcppThread/ThreadPool.hpp"
1314
#include "RcppThread/parallelFor.hpp"

inst/include/RcppThread/RMonitor.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class RMonitor {
3636
// user-facing functionality must be friends, so they can access
3737
// protected members of RMonitor.
3838
friend class RPrinter;
39+
friend class RErrPrinter;
3940
friend void checkUserInterrupt(bool condition);
4041
friend bool isInterrupted(bool condition);
4142

@@ -100,6 +101,23 @@ class RMonitor {
100101
}
101102
}
102103

104+
//! prints `object` to R error stream íf called from main thread; otherwise
105+
//! adds a printable version of `object` to a buffer for deferred printing.
106+
//! @param object a string or coercible object to print.
107+
template<class T>
108+
void safelyPrintErr(const T& object)
109+
{
110+
std::lock_guard<std::mutex> lk(m_);
111+
msgsErr_ << object;
112+
if ( calledFromMainThread() && (msgsErr_.str() != std::string("")) ) {
113+
// release messages in buffer
114+
REprintf("%s", msgsErr_.str().c_str());
115+
//R_FlushConsole();
116+
// clear message buffer
117+
msgsErr_.str("");
118+
}
119+
}
120+
103121
private:
104122
//! Ctors declared private, to instantiate class use `::instance()`.
105123
RMonitor(void) : isInterrupted_(false) {}
@@ -119,6 +137,7 @@ class RMonitor {
119137

120138
std::mutex m_; // mutex for synchronized r/w
121139
std::stringstream msgs_; // string buffer
140+
std::stringstream msgsErr_; // string buffer for stderr
122141
std::atomic_bool isInterrupted_;
123142
};
124143

inst/include/RcppThread/Rcerr.hpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright © 2021 Thomas Nagler
2+
//
3+
// This file is part of the RcppThread and licensed under the terms of
4+
// the MIT license. For a copy, see the LICENSE.md file in the root directory of
5+
// RcppThread or https://github.com/tnagler/RcppThread/blob/master/LICENSE.md.
6+
7+
#pragma once
8+
9+
#include <ostream>
10+
#include "RcppThread/RMonitor.hpp"
11+
12+
namespace RcppThread {
13+
14+
//! Safely printing to the R console from threaded code.
15+
class RErrPrinter {
16+
public:
17+
18+
//! prints `object` to R error stream íf called from main thread; otherwise
19+
//! adds a printable version of `object` to a buffer for deferred printing.
20+
//! @param object a string (or coercible object) to print.
21+
//! @details Declared as a friend in `RMonitor`.
22+
template<class T>
23+
RErrPrinter& operator<< (T& object)
24+
{
25+
RMonitor::instance().safelyPrintErr(object);
26+
return *this;
27+
}
28+
29+
//! prints `object` to R error stream íf called from main thread; otherwise
30+
//! adds a printable version of `object` to a buffer for deferred printing.
31+
//! @param object a string (or coercible object) to print.
32+
//! @details Declared as a friend in `RMonitor`.
33+
template<class T>
34+
RErrPrinter& operator<< (const T& object)
35+
{
36+
RMonitor::instance().safelyPrintErr(object);
37+
return *this;
38+
}
39+
40+
//! prints `object` to R error stream íf called from main thread; otherwise
41+
//! adds a printable version of `object` to a buffer for deferred printing.
42+
//! @param object a string (or coercible object) to print.
43+
//! @details Declared as a friend in `RMonitor`.
44+
RErrPrinter& operator<< (std::ostream& (*object)(std::ostream&))
45+
{
46+
RMonitor::instance().safelyPrintErr(object);
47+
return *this;
48+
}
49+
};
50+
51+
//! global `RPrinter` instance called 'Rcerr' (as in Rcpp).
52+
static RErrPrinter Rcerr = RErrPrinter();
53+
54+
}
55+
56+
// override std::cout to use RcppThread::Rcout instead
57+
#ifndef RCPPTHREAD_OVERRIDE_CERR
58+
#define RCPPTHREAD_OVERRIDE_CERR 0
59+
#endif
60+
61+
#if RCPPTHREAD_OVERRIDE_CERR
62+
#define cerr RcppThreadRcerr
63+
namespace std {
64+
static RcppThread::RErrPrinter RcppThreadRcerr = RcppThread::RErrPrinter();
65+
}
66+
#endif

inst/include/RcppThread/Thread.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "RcppThread/RMonitor.hpp"
1010
#include "RcppThread/Rcout.hpp"
11+
#include "RcppThread/Rcerr.hpp"
1112

1213
#include <thread>
1314
#include <future>
@@ -81,13 +82,15 @@ class Thread {
8182
auto timeout = std::chrono::milliseconds(250);
8283
while (future_.wait_for(timeout) != std::future_status::ready) {
8384
Rcout << "";
85+
Rcerr << "";
8486
if (isInterrupted())
8587
break;
8688
std::this_thread::yield();
8789
}
8890
if (thread_.joinable())
8991
thread_.join();
9092
Rcout << "";
93+
Rcerr << "";
9194
checkUserInterrupt();
9295
}
9396

inst/include/RcppThread/ThreadPool.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "RcppThread/RMonitor.hpp"
1010
#include "RcppThread/Rcout.hpp"
11+
#include "RcppThread/Rcerr.hpp"
1112
#include "RcppThread/quickpool.hpp"
1213

1314
#include <atomic>
@@ -232,10 +233,12 @@ ThreadPool::wait()
232233
do {
233234
pool_->wait(100);
234235
Rcout << "";
236+
Rcerr << "";
235237
checkUserInterrupt();
236238

237239
} while (!pool_->done());
238240
Rcout << "";
241+
Rcerr << "";
239242
}
240243

241244
//! waits for all jobs to finish.

0 commit comments

Comments
 (0)