Search code examples
rcflex-lexerstdiocran

How to get a FILE * to the R console from C


I am getting ready to submit an R package to CRAN and I am working through one final note from R CMD check. The note is that I have a reference to stdout. The "Writing R Extensions" manual has this to say about the problem:

Compiled code should not write to stdout or stderr and C++ and Fortran I/O should not be used. As with the previous item such calls may come from external software and may never be called, but package authors are often mistaken about that.

In all of my code I have made sure to call Rprintf or REprintf when I need to write something, but I have one more place where I can't make this switch: generated code from Flex.

I use GNU Flex/Bison to parse a DSL and Flex has its own pointers and references it uses for things like writing to stdout. Specifically, it has what it calls yyout which I can overwrite with the yyout_set function. Great! But what do I set it to?

The problem I have is that I can't change the (generated) C code to call REprintf, but I can't figure out if R exports a FILE * I can use to overwrite yyout. I tried digging around the R source code but I couldn't figure out where to look.

Is there a FILE * that I can use to write to the R console? If not, is there a sometimes acceptable solution? I haven't ever submitted a package to CRAN, but my impression is that this will not pass muster.


Solution

  • I have a two part solution to this problem that seems to do the trick. Please note, I am using re-entrant options for Flex/Bison, so this might not work exactly the same for everyone.

    I define ECHO to use Rprintf in the preamble of my lexer code.

    I actually already had code to define YY_FATAL_ERROR so I added the ECHO code to that. I added the following code to the preamble of my .l file:

    #ifdef __GNUC__
        /* this is for functions that don't return */
    #   define NORETURN __attribute__((noreturn))
    #else
    #   define NORETURN
    #endif
    
    void NORETURN Rf_error(const char *, ...);
    void Rprintf(const char *, ...);
    #define YY_FATAL_ERROR(msg) Rf_error( msg )
    #define ECHO Rprintf(yytext)
    

    Rf_error is what R's error calls, so it is the correct function for when there's an unrecoverable error. I went ahead and defined a macro for the GCC attribute to avoid warnings about the function not returning.

    I delete the lines from the generated source that set yyout to stdout.

    In the .yy.c file, in the YY_DECL function, delete the following lines:

                    if ( ! yyout )
                            yyout = stdout;
    

    I wrote a simple script to do this. If you don't regenerate this file very often you could do it by hand, I suppose.