Fixed a bug where matching on integer vectors with a non NA nomatch value
would still return NA.
Fixed a bug where getting r_df rows would return CHARSXP instead of STRSXP.
Fixed a bug where r_vec::apply would throw a compiler error when called on
lists and character vectors.
Sequences no longer abort on overflow, but instead silently return NA.
cpp_source now generates OpenMP flags, so its compiled functions
can use both OpenMP SIMD vectorisation and multi-threaded execution
(threads set via set_threads()).
New class r_function to safely call R functions from C++.
pkg_env is a helper that returns the environment of a package, allowing users
to easily retrieve functions from specific packages,
e.g. r_function("foo", pkg_env<"bar">()).
New copy member for r_vec, r_factors and r_df. copy shallow
copies the vector by creating a fresh copy of the atomic data, without deep
copying lists or attributes, just like Rf_shallow_duplicate.
r_sym now constructs symbols from strings (const char*, r_str,
r_str_view) under unwind protection, so R errors can no longer
longjmp past C++ destructors.
New by-group left-fold functional reduce_by_group, allowing for very
efficient binary reductions by-group.
Renamed common_length to list_common_length. common_length now
accepts initializer_list and variadic input.
New function list_recycle to recycle vectors of a list.
n_unique has been sped-up for integer vectors.
New initializer_list overloads for common helpers.
New r_sexp visit helpers visit_as and view_as.
Integer multiplication overflow handling is now more portable.
Improved type-safety regarding implicit coercion of RScalar types. They now can implicitly coerce only to their wrapped types, whereas before they could implicitly coerce to that as well as other C types.
RTimeType arithmetic has been deprecated and removed.
Arithmetic involving r_lgl is now always promoted to r_int, matching R's
own semantics.
common_math_t<T, U> now never returns r_lgl, effectively treating r_lgl
as r_int.
Fixed bug where exclusion of concepts header was affecting vignette creation on macos.
Fixed incorrect NA handling of r_date and r_psxct.
Fixed a bug where identical() would return false for two genuinely
identical NA_real_ values.
NULL optional arguments are now correctly handled.
Vector, factor and data frame arguments can now be NULL to allow for optional
argument programming from R.
Fixed a bug where checking exact equality (via identical) for C/C++ types
was not compiling due to template ordering issue.
r_lgl can now implicitly coerce to int (and only int), consistent with
the other RScalar types.
r_lgl values are now always normalised on construction internally to either
0, 1, or NA.
Integer overflow is now explicitly handled in all cppally arithmetic, returning
NA when detected. Integer addition and subtraction in particular are
written using branchless or vectorisable code, enabling SIMD vectorisation.
Integer in-place division /= now matches R's %/%
(floored division), instead of C's truncating division, e.g. -7L /= 2L now
gives -4 instead of -3. Division by zero or NA now correctly returns
NA instead of crashing the R session. Precision is also preserved for large
r_int64 values that previously round-tripped through double.
Scalar in-place arithmetic that narrows a wider result back to the left-hand
type (e.g. r_int += r_int64) now checks the value fits first,
giving NA on overflow instead of a silently wrapped result.
% now accepts mixed integer/float operands (e.g. r_int(7) % 2.5),
promoting and flooring consistent with R's %%.
Vector in-place arithmetic (+=, -=, *=, %=) now routes through the
scalar operators, fixing a bug where mixed-width operations (e.g.
r_vec<r_int> += r_vec<r_int64>) could silently truncate values, including
turning NA into 0.
Better errors when passing invalid types to template functions.
Constructing invalid dates and date-times now returns NA instead of
an error.
New concept RNumber to represent number-based types like r_int and r_dbl.
New member is_na for RScalar types, in addition to the equivalent
is_na free function.
New member function na() for RScalar classes.
Relational operators have been extended to cover all RNumericType classes
which includes r_date and r_psxct.
First major release. cppally's public API is now considered stable.
While there may be structural changes to the r_df and r_raw classes in the future,
cppally's vector and scalar classes are considered stable.
r_sexp.length has been deprecated, in favour of the free function length
length(r_df) now returns the number of rows instead of the number of cols, marking a shift in how cppally treats data frames. They are now seen as row-wise vectors.
Setting attributes on plain SEXP is now unsupported, e.g. via cppally::attr::set_attr. Use cppally types such as r_vector, r_factors, r_df and in some cases r_sexp for attribute manipulation.
Various out-of-place or trivial to implement r_vec member functions have been removed.
visit_vector, visit_sexp and view_sexp have been deprecated in favour of the more flexible constrained r_sexp visitors: r_sexp_visit, r_sexp_view and r_sexp_mutate. These allow concepts and custom constraints to be applied directly on the lambda's template parameter, e.g. r_sexp_visit(x, [&]<RVector T>(T vec){}) — here x is dispatched as its concrete vector type and aborts at runtime if the underlying type isn't an RVector. r_sexp_view is the non-owning sibling: the wrapper handed to the lambda is a view (no extra protect), so it must not outlive x. r_sexp_mutate is for in-place mutation: it moves x into the typed wrapper (making it the sole owner), calls f, then writes the result back.
r_factors elements are now treated as r_str in member functions like get and set
r_sexp_visit now visits r_null as r_vec<r_sexp>(r_null), essentially treating NULL as an empty list but without changing the underlying data.
For example, in the below pseudo-code, when x is r_null of type r_sexp, r_sexp_visit will disambiguate it as r_vec<r_sexp>(r_null), preserving its data as R's NULL but assigning its type as r_vec<r_sexp> (list).
r_sexp_visit(x, [&]<RVector T>(const T& vec) -> bool {
return vec.is_null();
});
This preservation behaviour is not new, in fact all r_vec<T> vectors preserve r_null by design, allowing for efficient and easier attribute manipulation with vectors that may or may not be r_null. What is new is that previously r_null was not a visitable r_sexp object and now it is.
r_df is now fully integrated into cppally.
New variadic function make_df to create in-line data frames.
Various r_df members have been added to allow easier data frame manipulation.
std::vector coercion. The following std::vector coercion directions are supported:
std::vector -> std::vectorstd::vector -> cppally::r_veccppally::r_vec -> std::vectorAny coercion between std::vector and cppally::r_vec is possible so long as the element coercions are supported by cppally::as
New function seq which behaves similarly to base::seq.
New function sequence which is similar to base::sequence but accepts only scalar inputs.
For named vectors, lookup by name has been dramatically improved in C++ by introducing a hashing approach. It works in the following way: the first time a lookup is requested, a linear scan is done to find the named value. The second time triggers the hash map of name-value pairs to be built and cached with the vector. That second lookup is completed using the cached hash map and all subsequent lookups also use the hash map. The rationale for hashing on second lookup is covered in the 'Automatic Names Hashing' vignette.
A similar hashing approach is also used for r_factors, making conversions of strings to and from factor codes fast and analytically viable.
cppally now supports copy-on-modify as an opt-in feature. This feature prevents accidentally overwriting data between shared objects, just like R. To opt-in, run cppally::use_copy_on_modify or set the copy_on_modify to TRUE in cpp_source.
The major downside of this feature is significantly slower element setting as every set must verify the object is not referenced by another object. This check is single-threaded and thus nearly all parallel cppally code is disabled as a safety precaution. If using copy-on-modify, it is recommended to avoid writing cppally registered R functions that rely on in-place modification.
Inspired by purrr::pmap and base::mapply, cppally::pmap is a C++ variadic function that supports applying custom C++ lambda functions across corresponding elements of multiple vectors.
With pmap it is trivial to calculate parallel statistics like max, min, etc. Example of C++ version of base::pmax applied to two vectors.
template <RVector T, RVector U>
requires requires(typename T::data_type a, typename U::data_type b) { max(a, b); }
[[cppally::register]]
auto cpp_pmax2(T x, U y){
return pmap([](auto a, auto b){ return max(a, b); }, x, y);
}
A left-fold reduction functional that successively applies a binary function along the elements of the vector (from left-to-right).
Example: maximum value across vector of doubles
[[cppally::register]]
r_dbl cpp_max(r_vec<r_dbl> x){
return x.reduce([](auto acc, auto curr){ return max(acc, curr); });
}
New alias of r_vec, r_vector.
Named-vector subsetting is now supported.
New C++ functions combine and flatten. combine is a variadic function that allows for combining multiple vectors into one, similar to base::c but always casts vectors to the common type among them. flatten allows one to flatten a list of vectors into one vector of a specified type, similar to unlist(recursive = FALSE).
Many functions that were originally r_vec-only members are now free functions that also work on r_sexp as well as RComposite types, allowing for easier manipulation of lists.
All C++ reference qualifiers (T&, T&&, const T&) are now supported for registered functions, including templated ones.
New concept RVectorisable which encompasses types that are OMP friendly.
New infix operator IS_IN, identical to R's %in%.
New C++ function coalesce().
r_psxct.datetime_str() always appends "UTC" at the end to avoid time-zone ambiguity.
When registering C++ functions, cppally.hpp is now included in the generated C++ code. Not including it caused issues when trying to compile functions that constructed factors.
Zero-length r_vec vectors can now be constructed unambiguously via r_vec<T>(0).
Math operations involving mixed types that included r_dbl are now correct when involving NA values.