4

I want to write a program to execute an R command using RInside and tell whether or not the result is a string. I created a RInside instance and used its method RInside::parseEval to parse the R command and then stored the result in a SEXP variable. I used TYPEOF to check if the type of the result is a string and then casted it to Rcpp::String using its constructor. The problem is that when I try to parse an error command such as paste('hello, the program's behavior becomes somewhat unpredictable. The program is showed below:

#include <RInside.h>
#include <stdio.h>

int main(int argc, char* argv[])
{

        RInside *R = new RInside();
        const char *expr = argv[1];

        SEXP res = NULL;
        try{
                res = R->parseEval(expr);
        } catch (std::exception &e) {
                res = NULL;
                printf("Exception thrown\n");
        }

        printf("res points to %p\n", res);
        if (res != NULL && TYPEOF(res) == STRSXP) {
                Rcpp::String res_str = res;
                printf("The result is a string: %s\n", res_str.get_cstring());
        } else {
                printf("The result is not a string");
        }
}

When I ran the program with the agrument "paste('hello'", I got the output

res points to 0x7f0ca84e14d0

The result is not a string

However, when I packed the code into a function, as shown:

#include <RInside.h>
#include <stdio.h>

void test_R(RInside *R, const char *expr)
{
        SEXP res = NULL;
        try{
                res = R->parseEval(expr);
        } catch (std::exception &e) {
                res = NULL;
                printf("Exception thrown\n");
        }

        printf("res points to %p\n", res);
        if (res != NULL && TYPEOF(res) == STRSXP) {
                Rcpp::String res_str = res;
                printf("The result is a string: %s\n", res_str.get_cstring());
        } else {
                printf("The result is not a string");
        }
}

int main(int argc, char* argv[])
{

        RInside *R = new RInside();
        const char *expr = argv[1];
        
        test_R(R, expr);
}

and ran it with the same command line argument "paste('hello'" as before, I got the output

res points to (nil)

The result is not a string

So my question is, why does it behave that way? When one parses an error command with RInside::parseEval, will an exception be thrown or will the result be a NULL pointer or will at least one of those cases happen? Does my code above properly do its job?

Any help will be thankful.

Edit 0:

After spending some time to read the code, it appears that R treats the command paste('hello as "incomplete", which means that if we later send another command to "complete" it, it will be successfully executed.

#include <RInside.h>
#include <stdio.h>

void test_R(RInside *R, const char *expr, int is_ok)
{
        SEXP res = R->parseEval(expr);
        if (is_ok) {
                std::string res_str = Rcpp::String(res);
                printf("The result is %s\n", res_str.c_str());
        }
}

int main()
{

        RInside R;
        const char *head = "paste('hello";
        const char *tail = "')";

        test_R(&R, head, 0); // This will parse "paste('hello"
        test_R(&R, tail, 1); // This will parse the rest of the command
}

The result of the code above is

The result is hello

Community
  • 1
  • 1
AnhHao Trần
  • 145
  • 1
  • 10
  • Yes, it is exactly like the R command prompt. Or like reading an R file line by line. An incomplete statement breaks the _current_ parse but does not prevent subsequent parsing. If an error occurs, we throw a C++ exception. Or you call us in non-exception. All of this has been there for a decade and worked just fine for most people... – Dirk Eddelbuettel Feb 24 '20 at 17:18

0 Answers0