feat: Use safe accessor to vector elements#1633
Conversation
Current Aviator status
This PR was merged manually (without Aviator). Merging manually can negatively impact the performance of the queue. Consider using Aviator next time.
See the real-time status of this PR on the
Aviator webapp.
Use the Aviator Chrome Extension
to see the status of your PR within GitHub.
|
|
There are two things that bother me in the implementation:
I just wanted to note these potential issues. Proceed as you see fit. |
src/rinterface_extra.c
Outdated
| igraph_errorf("Wrong index. Attempt to get element with index %" PRIuPTR " from vector of length %" PRIuPTR ".", | ||
| __FILE__, __LINE__, IGRAPH_EINVAL, (uintptr_t) index, (uintptr_t) Rf_xlength(sexp)); |
There was a problem hiding this comment.
Perhaps:
| igraph_errorf("Wrong index. Attempt to get element with index %" PRIuPTR " from vector of length %" PRIuPTR ".", | |
| __FILE__, __LINE__, IGRAPH_EINVAL, (uintptr_t) index, (uintptr_t) Rf_xlength(sexp)); | |
| Rf_error("Wrong index. Attempt to get element with index %" PRIuPTR " from vector of length %" PRIuPTR ".", | |
| file, line, IGRAPH_EINVAL, (uintptr_t) index, (uintptr_t) Rf_xlength(sexp)); |
with file and line passed from the invocation point (e.g., https://github.com/igraph/rigraph/pull/1633/files#diff-44d6b09dbc4b7f024f1b2df8dcc51a6e3d489408a6f82def8dd49ffb1bcd5dc4R4610) .
There was a problem hiding this comment.
This won't properly unwind the "finally" stack.
There was a problem hiding this comment.
This is still better than undefined behavior.
I don't have a good answer here. Let's draft and iterate.
There was a problem hiding this comment.
This won't properly unwind the "finally" stack.
Is it not only the way to properly unwind the finally stack is to call igraph_error() directly?
There was a problem hiding this comment.
In the C core we use this approach:
igraph_error_t R_get_int_scalar(SEXP sexp, R_xlen_t index, igraph_integer_t *res) {
if (Rf_xlength(sexp) <= index) {
IGRAPH_ERRORF("Wrong index. Attempt to get element with index %" PRIuPTR " from vector of length %" PRIuPTR ".", IGRAPH_EINVAL, (uintptr_t) index, (uintptr_t) Rf_xlength(sexp));
}
*res = (igraph_integer_t)REAL(sexp)[index];
return IGRAPH_SUCCESS;
}Then R_get_int_scalar returns an error code as usual, and can be wrapped in IGRAPH_CHECK or IGRAPH_R_CHECK.
Would this work for you?
There was a problem hiding this comment.
In the C core we use this approach:
igraph_error_t R_get_int_scalar(SEXP sexp, R_xlen_t index, igraph_integer_t *res) { if (Rf_xlength(sexp) <= index) { IGRAPH_ERRORF("Wrong index. Attempt to get element with index %" PRIuPTR " from vector of length %" PRIuPTR ".", IGRAPH_EINVAL, (uintptr_t) index, (uintptr_t) Rf_xlength(sexp)); } *res = (igraph_integer_t)REAL(sexp)[index]; return IGRAPH_SUCCESS; }Then
R_get_int_scalarreturns an error code as usual, and can be wrapped inIGRAPH_CHECKorIGRAPH_R_CHECK.Would this work for you?
Yes, this is what I was thinking about. Thanks.
I'll try this way.
There was a problem hiding this comment.
Is it not only the way to properly unwind the
finallystack is to calligraph_error()directly?
Yes in the sense that igraph_error() should be used, but normally it is called through IGRAPH_ERROR, which ensures returning from the function with an error code, which can then be processed by IGRAPH_CHECK.
In igraph 0.10, the unwinding process can potentially have multiple stages, each of which need to be triggered appropriately. This means that igraph_error() should not blindly longjmp. That might leave some stages untriggered.
This is why in the R interface we have IGRAPH_R_CHECK which must only be used at the top level. Only at this level is it safe to longjmp from the error handler, i.e. to call Rf_error(). IGRAPH_R_CHECK controls whether Rf_error() is called by igraph_error() by setting a global flag.
Now one might argue that R_get_int_scalar() is (or should be) only called at the top level anyway, so we can forget all about this. (Note that I am not actually sure whether this is true, there might be some non-top-level functions where things like REAL(sexp)[0] are still used!) If you decide to not worry about this, there's a risk that someone will use R_get_int_scalar() in an inappropriate context in the future, which causes a bug that's only triggered in an error condition, and will be very painful to track down. Thus if you go this route, I recommend clearly documenting what check function is allowed to be called in what context, and perhaps even naming these functions in such a way that any misuse is immediately apparent.
There was a problem hiding this comment.
Thus if you go this route, I recommend clearly documenting what check function is allowed to be called in what context, and perhaps even naming these functions in such a way that any misuse is immediately apparent.
Not sure right now how to manage this, but I would think that functions which are have SEXP type as argument should be used only in top level functions. Which is also the case for R_get_int_scalar(). Maybe it make sense.
There was a problem hiding this comment.
I agree that R_get_int_scalar() and friends should only be called at the top level and before any finalizers are set up. Also agree to document the purpose and the constraints.
Co-authored-by: Kirill Müller <krlmlr@users.noreply.github.com>
Co-authored-by: Kirill Müller <krlmlr@users.noreply.github.com>
|
Thanks! |
For #1631 .