Skip to content

Commit 53f927f

Browse files
committed
slightly better headers
1 parent e00a58f commit 53f927f

24 files changed

+1854
-498
lines changed

inst/include/cpp4r/R.hpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@
3636

3737
#if defined(R_VERSION) && R_VERSION >= R_Version(4, 4, 0)
3838
// Use R's new macro
39-
#define cpp4r_PRIdXLEN_T R_PRIdXLEN_T
39+
#define CPP4R_PRIdXLEN_T R_PRIdXLEN_T
4040
#else
4141
// Recreate what new R does
4242
#ifdef LONG_VECTOR_SUPPORT
43-
#define cpp4r_PRIdXLEN_T "td"
43+
#define CPP4R_PRIdXLEN_T "td"
4444
#else
45-
#define cpp4r_PRIdXLEN_T "d"
45+
#define CPP4R_PRIdXLEN_T "d"
4646
#endif
4747
#endif
4848

@@ -64,7 +64,7 @@ namespace detail {
6464

6565
// Annoyingly, `TYPEOF()` returns an `int` rather than a `SEXPTYPE`,
6666
// which can throw warnings with `-Wsign-compare` on Windows.
67-
inline SEXPTYPE r_typeof(SEXP x) { return static_cast<SEXPTYPE>(TYPEOF(x)); }
67+
inline SEXPTYPE r_typeof(SEXP x) noexcept { return static_cast<SEXPTYPE>(TYPEOF(x)); }
6868

6969
/// Get an object from an environment
7070
///
@@ -82,6 +82,7 @@ inline SEXP r_env_get(SEXP env, SEXP sym) {
8282
// - `R_MissingArg` can't leak from an `env` anymore
8383
// - Promises can't leak from an `env` anymore
8484

85+
// Most lookups succeed, so optimize for the common case
8586
if (out == R_MissingArg) {
8687
Rf_errorcall(R_NilValue, "argument \"%s\" is missing, with no default",
8788
CHAR(PRINTNAME(sym)));
@@ -121,15 +122,52 @@ inline T na();
121122
template <typename T>
122123
inline typename std::enable_if<!std::is_same<typename std::decay<T>::type, double>::value,
123124
bool>::type
124-
is_na(const T& value) {
125+
is_na(const T& value) noexcept {
125126
return value == na<T>();
126127
}
127128

128129
template <typename T>
129130
inline typename std::enable_if<std::is_same<typename std::decay<T>::type, double>::value,
130131
bool>::type
131-
is_na(const T& value) {
132+
is_na(const T& value) noexcept {
132133
return ISNA(value);
133134
}
134135

136+
// Fast utility functions for common SEXP operations
137+
inline bool is_null(SEXP x) noexcept { return x == R_NilValue; }
138+
139+
inline bool is_scalar(SEXP x) noexcept { return Rf_length(x) == 1; }
140+
141+
inline bool is_vector_type(SEXPTYPE type) noexcept {
142+
// Most common vector types first for better branch prediction
143+
return type == REALSXP || type == INTSXP || type == LGLSXP || type == STRSXP ||
144+
type == CPLXSXP || type == RAWSXP;
145+
}
146+
147+
inline bool is_atomic(SEXP x) noexcept { return is_vector_type(detail::r_typeof(x)); }
148+
149+
// Fast length check with early return for null
150+
inline R_xlen_t safe_length(SEXP x) noexcept { return is_null(x) ? 0 : Rf_length(x); }
151+
152+
// Optimized type checking with branch prediction
153+
inline bool is_real(SEXP x) noexcept { return detail::r_typeof(x) == REALSXP; }
154+
155+
inline bool is_integer(SEXP x) noexcept { return detail::r_typeof(x) == INTSXP; }
156+
157+
inline bool is_logical(SEXP x) noexcept { return detail::r_typeof(x) == LGLSXP; }
158+
159+
inline bool is_character(SEXP x) noexcept { return detail::r_typeof(x) == STRSXP; }
160+
161+
inline bool is_complex(SEXP x) noexcept { return detail::r_typeof(x) == CPLXSXP; }
162+
163+
inline bool is_raw(SEXP x) noexcept { return detail::r_typeof(x) == RAWSXP; }
164+
165+
// Fast environment checking
166+
inline bool is_environment(SEXP x) noexcept { return detail::r_typeof(x) == ENVSXP; }
167+
168+
inline bool is_function(SEXP x) noexcept {
169+
SEXPTYPE type = detail::r_typeof(x);
170+
return type == CLOSXP || type == BUILTINSXP || type == SPECIALSXP;
171+
}
172+
135173
} // namespace cpp4r

inst/include/cpp4r/as.hpp

Lines changed: 51 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ struct is_std_complex<std::complex<T>> : std::true_type {};
8585

8686
// https://stackoverflow.com/a/1521682/2055486
8787
//
88-
inline bool is_convertible_without_loss_to_integer(double value) {
88+
inline bool is_convertible_without_loss_to_integer(double value) noexcept {
8989
double int_part;
9090
return std::modf(value, &int_part) == 0.0;
9191
}
@@ -102,25 +102,23 @@ enable_if_is_sexp<T, T> as_cpp(SEXP from) {
102102

103103
template <typename T>
104104
enable_if_integral<T, T> as_cpp(SEXP from) {
105-
if (Rf_isInteger(from)) {
106-
if (Rf_xlength(from) == 1) {
107-
return INTEGER_ELT(from, 0);
105+
if (__builtin_expect(Rf_xlength(from) != 1, 0)) {
106+
throw std::length_error("Expected single integer value");
107+
}
108+
109+
if (__builtin_expect(Rf_isInteger(from), 1)) {
110+
return INTEGER_ELT(from, 0);
111+
} else if (__builtin_expect(Rf_isReal(from), 0)) {
112+
if (__builtin_expect(ISNA(REAL_ELT(from, 0)), 0)) {
113+
return NA_INTEGER;
108114
}
109-
} else if (Rf_isReal(from)) {
110-
if (Rf_xlength(from) == 1) {
111-
if (ISNA(REAL_ELT(from, 0))) {
112-
return NA_INTEGER;
113-
}
114-
double value = REAL_ELT(from, 0);
115-
if (is_convertible_without_loss_to_integer(value)) {
116-
return value;
117-
}
115+
double value = REAL_ELT(from, 0);
116+
if (__builtin_expect(is_convertible_without_loss_to_integer(value), 1)) {
117+
return value;
118118
}
119-
} else if (Rf_isLogical(from)) {
120-
if (Rf_xlength(from) == 1) {
121-
if (LOGICAL_ELT(from, 0) == NA_LOGICAL) {
122-
return NA_INTEGER;
123-
}
119+
} else if (__builtin_expect(Rf_isLogical(from), 0)) {
120+
if (__builtin_expect(LOGICAL_ELT(from, 0) == NA_LOGICAL, 0)) {
121+
return NA_INTEGER;
124122
}
125123
}
126124

@@ -143,77 +141,63 @@ enable_if_enum<E, E> as_cpp(SEXP from) {
143141

144142
template <typename T>
145143
enable_if_bool<T, T> as_cpp(SEXP from) {
146-
if (Rf_isLogical(from)) {
147-
if (Rf_xlength(from) == 1) {
148-
return LOGICAL_ELT(from, 0) == 1;
149-
}
144+
if (__builtin_expect(Rf_isLogical(from) && Rf_xlength(from) == 1, 1)) {
145+
return LOGICAL_ELT(from, 0) == 1;
150146
}
151147

152148
throw std::length_error("Expected single logical value");
153149
}
154150

155151
template <typename T>
156152
enable_if_floating_point<T, T> as_cpp(SEXP from) {
157-
if (Rf_isReal(from)) {
158-
if (Rf_xlength(from) == 1) {
159-
return REAL_ELT(from, 0);
160-
}
153+
if (__builtin_expect(Rf_xlength(from) != 1, 0)) {
154+
throw std::length_error("Expected single double value");
155+
}
156+
157+
if (__builtin_expect(Rf_isReal(from), 1)) {
158+
return REAL_ELT(from, 0);
161159
}
162160
// All 32 bit integers can be coerced to doubles, so we just convert them.
163-
if (Rf_isInteger(from)) {
164-
if (Rf_xlength(from) == 1) {
165-
if (INTEGER_ELT(from, 0) == NA_INTEGER) {
166-
return NA_REAL;
167-
}
168-
return INTEGER_ELT(from, 0);
161+
if (__builtin_expect(Rf_isInteger(from), 0)) {
162+
if (__builtin_expect(INTEGER_ELT(from, 0) == NA_INTEGER, 0)) {
163+
return NA_REAL;
169164
}
165+
return INTEGER_ELT(from, 0);
170166
}
171167

172168
// Also allow NA values
173-
if (Rf_isLogical(from)) {
174-
if (Rf_xlength(from) == 1) {
175-
if (LOGICAL_ELT(from, 0) == NA_LOGICAL) {
176-
return NA_REAL;
177-
}
169+
if (__builtin_expect(Rf_isLogical(from), 0)) {
170+
if (__builtin_expect(LOGICAL_ELT(from, 0) == NA_LOGICAL, 0)) {
171+
return NA_REAL;
178172
}
179173
}
180174

181175
throw std::length_error("Expected single double value");
182176
}
183177

184-
// Definition for converting SEXP to std::complex<double>
185-
inline std::complex<double> as_cpp(SEXP x) {
186-
if (TYPEOF(x) != CPLXSXP || Rf_length(x) != 1) {
187-
throw std::invalid_argument("Expected a single complex number.");
188-
}
189-
Rcomplex c = COMPLEX(x)[0];
190-
return {c.r, c.i};
191-
}
178+
// Removed generic complex template to avoid ambiguity - use specific specializations
179+
// instead
192180

193181
template <typename T>
194182
enable_if_char<T, T> as_cpp(SEXP from) {
195-
if (Rf_isString(from)) {
196-
if (Rf_xlength(from) == 1) {
197-
return unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0))[0]; });
198-
}
183+
if (__builtin_expect(Rf_isString(from) && Rf_xlength(from) == 1, 1)) {
184+
return unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0))[0]; });
199185
}
200186

201187
throw std::length_error("Expected string vector of length 1");
202188
}
203189

204190
template <typename T>
205191
enable_if_c_string<T, T> as_cpp(SEXP from) {
206-
if (Rf_isString(from)) {
207-
if (Rf_xlength(from) == 1) {
208-
void* vmax = vmaxget();
192+
if (__builtin_expect(Rf_isString(from) && Rf_xlength(from) == 1, 1)) {
193+
void* vmax = vmaxget();
209194

210-
const char* result =
211-
unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0)); });
195+
const char* result =
196+
unwind_protect([&] { return Rf_translateCharUTF8(STRING_ELT(from, 0)); });
212197

213-
vmaxset(vmax);
198+
vmaxset(vmax);
214199

215-
return {result};
216-
}
200+
return {result};
217201
}
218202

219203
throw std::length_error("Expected string vector of length 1");
@@ -224,33 +208,28 @@ enable_if_std_string<T, T> as_cpp(SEXP from) {
224208
return {as_cpp<const char*>(from)};
225209
}
226210

227-
/// Temporary workaround for compatibility with cpp4r 0.1.0
211+
// Specialization for converting std::complex<T> to SEXP
228212
template <typename T>
229-
enable_if_t<!std::is_same<decay_t<T>, T>::value, decay_t<T>> as_cpp(SEXP from) {
230-
return as_cpp<decay_t<T>>(from);
231-
}
232-
233-
// Specialization for converting std::complex<double> to SEXP
234-
inline SEXP as_sexp(const std::complex<double>& x) {
213+
inline SEXP as_sexp(const std::complex<T>& x) {
235214
SEXP result = PROTECT(Rf_allocVector(CPLXSXP, 1));
236-
COMPLEX(result)[0].r = x.real();
237-
COMPLEX(result)[0].i = x.imag();
215+
COMPLEX(result)[0].r = static_cast<double>(x.real());
216+
COMPLEX(result)[0].i = static_cast<double>(x.imag());
238217
UNPROTECT(1);
239218
return result;
240219
}
241220

242221
template <typename T>
243-
enable_if_integral<T, SEXP> as_sexp(T from) {
222+
enable_if_integral<T, SEXP> as_sexp(T from) noexcept {
244223
return safe[Rf_ScalarInteger](from);
245224
}
246225

247226
template <typename T>
248-
enable_if_floating_point<T, SEXP> as_sexp(T from) {
227+
enable_if_floating_point<T, SEXP> as_sexp(T from) noexcept {
249228
return safe[Rf_ScalarReal](from);
250229
}
251230

252231
template <typename T>
253-
enable_if_bool<T, SEXP> as_sexp(T from) {
232+
enable_if_bool<T, SEXP> as_sexp(T from) noexcept {
254233
return safe[Rf_ScalarLogical](from);
255234
}
256235

@@ -272,7 +251,7 @@ enable_if_integral<T, SEXP> as_sexp(const Container& from) {
272251

273252
auto it = from.begin();
274253
int* data_p = INTEGER(data);
275-
for (R_xlen_t i = 0; i < size; ++i, ++it) {
254+
for (R_xlen_t i = 0; __builtin_expect(i < size, 1); ++i, ++it) {
276255
data_p[i] = *it;
277256
}
278257
return data;
@@ -291,7 +270,7 @@ enable_if_floating_point<T, SEXP> as_sexp(const Container& from) {
291270

292271
auto it = from.begin();
293272
double* data_p = REAL(data);
294-
for (R_xlen_t i = 0; i < size; ++i, ++it) {
273+
for (R_xlen_t i = 0; __builtin_expect(i < size, 1); ++i, ++it) {
295274
data_p[i] = *it;
296275
}
297276
return data;
@@ -310,7 +289,7 @@ enable_if_bool<T, SEXP> as_sexp(const Container& from) {
310289

311290
auto it = from.begin();
312291
int* data_p = LOGICAL(data);
313-
for (R_xlen_t i = 0; i < size; ++i, ++it) {
292+
for (R_xlen_t i = 0; __builtin_expect(i < size, 1); ++i, ++it) {
314293
data_p[i] = *it;
315294
}
316295
return data;

inst/include/cpp4r/attribute_proxy.hpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <initializer_list> // for initializer_list
44
#include <string> // for string, basic_string
5+
#include <utility> // for std::forward
56

67
#include "cpp4r/R.hpp" // for SEXP, SEXPREC, Rf_install, PROTECT, Rf_...
78
#include "cpp4r/as.hpp" // for as_sexp
@@ -18,31 +19,38 @@ class attribute_proxy {
1819
SEXP symbol_;
1920

2021
public:
21-
attribute_proxy(const T& parent, const char* index)
22+
inline attribute_proxy(const T& parent, const char* index)
2223
: parent_(parent), symbol_(safe[Rf_install](index)) {}
2324

24-
attribute_proxy(const T& parent, const std::string& index)
25+
inline attribute_proxy(const T& parent, const std::string& index)
2526
: parent_(parent), symbol_(safe[Rf_install](index.c_str())) {}
2627

27-
attribute_proxy(const T& parent, SEXP index) : parent_(parent), symbol_(index) {}
28+
inline attribute_proxy(const T& parent, SEXP index) noexcept
29+
: parent_(parent), symbol_(index) {}
2830

2931
template <typename C>
30-
attribute_proxy& operator=(C rhs) {
31-
SEXP value = PROTECT(as_sexp(rhs));
32-
Rf_setAttrib(parent_.data(), symbol_, value);
33-
UNPROTECT(1);
32+
inline attribute_proxy& operator=(C&& rhs) {
33+
// Universal reference for optimal forwarding
34+
unwind_protect([&] {
35+
SEXP value = as_sexp(std::forward<C>(rhs));
36+
Rf_setAttrib(parent_.data(), symbol_, value);
37+
});
3438
return *this;
3539
}
3640

3741
template <typename C>
38-
attribute_proxy& operator=(std::initializer_list<C> rhs) {
39-
SEXP value = PROTECT(as_sexp(rhs));
40-
Rf_setAttrib(parent_.data(), symbol_, value);
41-
UNPROTECT(1);
42+
inline attribute_proxy& operator=(std::initializer_list<C> rhs) {
43+
// Use unwind_protect for better exception safety and potentially faster execution
44+
unwind_protect([&] {
45+
SEXP value = as_sexp(rhs);
46+
Rf_setAttrib(parent_.data(), symbol_, value);
47+
});
4248
return *this;
4349
}
4450

45-
operator SEXP() const { return safe[Rf_getAttrib](parent_.data(), symbol_); }
51+
inline operator SEXP() const noexcept {
52+
return safe[Rf_getAttrib](parent_.data(), symbol_);
53+
}
4654
};
4755

4856
} // namespace cpp4r

0 commit comments

Comments
 (0)