Looking at the discussion of bounds checking in "Element Access" unoffical API docs by @coatless:
"Note: Unlike R, there is no [] subset operator for matrices with C++"
At present, operator[] compiles with any number of args, both for Vector and Matrix.
I tried looking at the doxygen docs to figure out what was actually happening here.
Focusing on Vector, I see:
Questions:
As per the unofficial docs referenced above, operator() checks bounds for 1 arg (Vector), but not 2 args (Matrix). With an eye to best practices, is it practical for:
This would permit (unofficially) deprecating operator[], and rather simplify explanations.
library(Rcpp)
sourceCpp('checkops.cpp');
checkops(a_mat=matrix(1:9, ncol=3), a_vec=1:20)
checkops.cpp:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
void checkops(NumericMatrix a_mat, NumericVector a_vec){
// no BC
auto aa = a_mat(1,2);
// not defined?
// auto aa1 = a_mat(1);
// BC
auto bb = a_mat.at(1,2);
// not defined?
//auto bb = a_mat.at(1);
// fails correctly
// auto bb2 = a_mat.at(1,2,3);
// compiles, badsauce
auto cc = a_mat[1,2];
// ??
auto dd = a_mat[1,2,3];
// vector
// BC
auto ff = a_vec(1);
// also BC, identical to above
auto gg = a_vec.at(1);
// fails correctly
//auto hh = a_vec.at(1,2);
// No BC
auto ii = a_vec[1];
// compiles, badsauce
auto jj = a_vec[1,2];
// output
Rcout << "Matrix: " << aa << bb << cc << dd << std::endl ;
//
Rcout << "Vector: " << ff << gg << ii << jj << std::endl;
}
Regarding
Is it possible for operator[] to not compile (or to throw) when given too many args, without breaking existing code?
The short answer is probably not, and not for any reason inherent to the design of these classes / their definition of operator[], but rather due to how operator, behaves in standard C++ itself. When you write a_mat[1,2,3], 1 and 2 are "silently" discarded, resulting in the expression a_mat[3], which is valid.
See the explanation here:
In a comma expression E1, E2, the expression E1 is evaluated, its result is discarded, and its side effects are completed before evaluation of the expression E2 begins [...]
For example,
#include <Rcpp.h>
// [[Rcpp::export]]
double bounds() {
Rcpp::NumericMatrix m(2, 2);
m.column(0) = Rcpp::NumericVector::create(1, 1);
m.column(1) = Rcpp::NumericVector::create(2, 2);
return m[0, 1, 2]; // evaluates as m::operator[](2)
}
/*** R
R> bounds()
# [1] 2
*/
If you are compiling with the -Wall flag (which you always should be), you will be warned about this:
rcppbounds2.cpp:9:17: warning: left operand of comma operator has no effect [-Wunused-value]
return m[0, 1, 2]; // evaluates as m::operator[2]
^
rcppbounds2.cpp:9:20: warning: right operand of comma operator has no effect [-Wunused-value]
return m[0, 1, 2]; // evaluates as m::operator[2]
Subsequently, regarding
If not 3., then might it make sense to strongly recommending against using operator[], particularly for matrices, possibly to the extent of deprecating it? The current behavior seems like trouble waiting to happen.
In my opinion this is a non-issue, because
One can easily check the source code (either the generated Doxygen files, or in this repository) and see that both signatures of Matrix::operator[] accept a single index argument.
A reasonably experienced C++ programmer will be aware of the behavior of operator, (and most likely have checked the source code as well).
Spot on regarding the comma operator. Goes all the way back to K&R C.
That probably means we can close this, no?
And not to nitpick, but at the moment the unofficial API docs you've cited are, well, unofficial, and there is still a fair bit more to be done (in terms of breadth of content as well as the actual verbiage used) before they are ready to be promoted as official documentation. Having said that, your feedback is much appreciated and will certainly be taken into account during further development of the documentation.
Thanks for clarifying the parse - apologies, I looked for exactly that.
I understand this particular documentation is unofficial - I'm pointing at in primarily to clarify existing & intended functionality.
Can you comment on the remaining points? In particular, are the following intentional / desirable?
As an end-user, I quite like the the approach used by Armadillo, where the behavior of operator() and at() is consistent over both class and argument number. This would allow somewhat less experienced C++ programmers (or run-of-the-mill R users) to altogether ignore operator[] :)
Most helpful comment
And not to nitpick, but at the moment the unofficial API docs you've cited are, well, unofficial, and there is still a fair bit more to be done (in terms of breadth of content as well as the actual verbiage used) before they are ready to be promoted as official documentation. Having said that, your feedback is much appreciated and will certainly be taken into account during further development of the documentation.