The C/C++ #define
mechanism is a very powerful, but very blunt templating approach. It’s a simple textual search-and-replace mechanism, which can be very useful, but not so subtle.
This was illustrated to me today when I ran into a problem with some code for an R extension that I am writing, that interfaces to Reuters market data feeds. The offending code is shown below:
RTRXFileDb* configDb = new RTRXFileDb(configPath);
if (configDb->error()) {
Rf_error("Error initializing SFC: %s", configDb->errorText());
return ScalarReal(-1);
}
This code creates an object instance, and checks the boolean member property error()
property for problems. If there is a problem, it calls the R function error()
to print the error to the R console.
When I tried to compile this code, however, I got the following error:
error C2039: ‘Rf_error’ : is not a member of ‘RTRXFileDb’
This initially confused me, but then I looked at the R header file error.h and found the definition of error():
#define R_ERROR_H_
#ifdef __cplusplus
extern "C" {
#endif
void Rf_error(const char *, ...);
void Rf_warning(const char *, ...);
void WrongArgCount(const char *);
void UNIMPLEMENTED(const char *);
void R_ShowMessage(const char *s);
#ifdef __cplusplus
}
#endif
#ifndef R_NO_REMAP
#define error Rf_error
#define warning Rf_warning
#endif
#endif /* R_ERROR_H_ */
So error is mapped to Rf_error via a #define
directive, meaning that whenever the preprocessor encountered the “error” token, it went ahead and replaced it with the R-mapped definition, regardless of its syntactic context. This is normally not a problem, apart from when we get name collisions, as we have here. There are a few potential ways to fix this, none of them ideal. Some of the options are:
#define R_NO_REMAP
in the preprocessor, which will stoperror
being mapped toRf_error
, and just useRf_error
explicitly. The problem with this approach is thatR_NO_REMAP
will undefine a whole bunch of other R functions, making it tedious to replace every one.- Change the R header file: either change the #define block to map
Rf_error
to_error
, or some alternative name, and just change the usages of that, or alternatively remove theerror
mapping completely. - Use
#undef
error and just useRf_error
explicitly (Pointed out on Reddit)
I’m going to go for one of the latter options, as they are the path of least resistance. This was actually a very small issue. But name collisions of this type can potentially be nasty, or at the very least tedious to fix.