Skip to content

Dealing with printf() &c. in third-party library code

5 messages · Jon Clayden, Martin Morgan

#
Dear all,

I recognise the reason for strongly discouraging use of printf() and
similar C functions in R packages, but I wonder what people do in
practice about third-party code which may be littered with such calls.
I maintain a package (RNiftyReg) which provides an R interface to a
third-party library which contains hundreds of calls to printf(...),
fprintf(stderr,...) and similar. It seems to me that there are several
possible approaches, but all have their issues:

1. Replace all such calls with equivalent Rprintf() calls, using
compiler preprocessing directives to ensure the library does not
become incompatible with other code. For example,

#ifdef RNIFTYREG
Rprintf(...);
#else
printf(...);
#endif

This will be very time-consuming if there are lots of calls, and also
makes the code very untidy and much harder to update when a new
version of the upstream library is released.

2. Remove all such calls from the code altogether, or comment them
out. The problem here is that doing this safely is hard, because the
call could be part of an "if" statement or similar. For example,

if (test)
 printf("Something");
do_something_important;

If the middle line here is removed, then the last line becomes
(erroneously) conditioned on the test. Plus, once again, you are
introducing a lot of small changes to the library itself.

3. Redefine printf to use Rprintf, viz.

#ifdef RNIFTYREG
#include <R.h>
#define printf Rprintf
#endif

This will compile as long as the R function is a drop-in replacement
for the original function, which I believe is true for Rprintf (vs.
printf), but isn't true for Calloc (vs. calloc), for example. And I'm
not sure whether this approach can be used to deal with cases of the
form fprintf(stderr,...), where stderr would need to be redefined.
This approach requires only modest changes to the library itself, but
may be fragile to future changes in R.

Are there any other (better?) alternatives? Any thoughts or advice
would be appreciated.

All the best,
Jon
#
On 03/14/2012 05:15 AM, Jon Clayden wrote:
In Makevars, I add -Dfprintf=my_fprintf to the pre-processor flags and 
then implement my_fprintf in a separate source file. This means that the 
source code of the 3rd party library is not touched, and there is some 
scope for re-mapping or otherwise intercepting function arguments. For 
abort and error, I throw an error that encourages the user to save and 
quit immediately, though this is far from ideal. I too would be 
interested in better practices for dealing with this, short of 
whole-sale modification of the third-party library.

Martin

  
    
#
Martin,

Thanks for your reply. I wonder if you'd be willing to post your
"my_fprintf" function, since I'm struggling to get around needing to
use the "stdout" and "stderr" symbols completely. This function has
the right effect...

void rniftyreg_fprintf (FILE *stream, const char *format, ...)
{
   va_list args;
   va_start(args, format);

   if (stream == stdout)
       Rvprintf(format, args);
   else if (stream == stderr)
       REvprintf(format, args);
   else
       vfprintf(stream, format, args);

   va_end(args);
}

... but the R CMD check info message still arises because stdout and
stderr still appear. I'm struggling to see how to get around this
without doing something really ugly, like casting integers to FILE*
pointers.

All the best,
Jon
On 15 March 2012 05:04, Martin Morgan <mtmorgan at fhcrc.org> wrote:
#
On 03/15/2012 02:24 PM, Jon Clayden wrote:
Hi Jon --

My own implementation is like yours, where I still reference stderr / 
stdout. But it seems like the meaningful problem (writing to stderr / 
stdout, which R might have re-directed) has been addressed (except for 
the third branch, in your code above).

Martin

  
    
#
On 16 March 2012 00:48, Martin Morgan <mtmorgan at fhcrc.org> wrote:
Yes, this certainly deals with the important issue - the check note
becomes a false positive. I need the third branch to allow things to
work properly for fprintf calls which write to actual files...

Thanks again,
Jon