Мой код, который пытается эмулировать оболочку R через C ++, позволяет пользователю отправлять команды R через соединение tcp, которые затем передаются экземпляру R через функцию RInside :: parseEvalQ во время выполнения. Я должен уметь обрабатывать плохо отформатированные команды. Всякий раз, когда в качестве аргумента для parseEvalQ дается неверная команда, я улавливаю выдаваемую ошибку времени выполнения (глядя на RInside.cpp, моя конкретная ошибка помечается статусом 'PARSE_ERROR' 'внутри функции parseEval (const string &, SEXP)), чем () дает исключение "St9exception".
У меня две проблемы, первая более насущная, чем вторая:
1а. После первоначальной ошибки разбора любой последующий вызов parseEvalQ приводит к другой ошибке разбора, даже если аргумент действителен. Не поврежден ли встроенный экземпляр R из-за ошибки разбора?
1б. Документация RInside рекомендует использовать Rcpp :: Evaluator :: run для обработки исключений R в C ++ (которые, как я подозреваю, генерируются где-то в экземпляре R во время вызова parseEval (const string &, SEXP), до того, как он вернет статус ошибки 'PARSE_ERROR «). Я экспериментировал, пытаясь использовать это, но не могу найти в Интернете примеров того, как практически использовать Rcpp :: Evaluator :: run.
2. В моей программе я перенаправляю stdout и stderr (на уровне C ++) на файловый дескриптор моего tcp-соединения, любые сообщения об ошибках из экземпляра RInside отправляются на консоль, а обычные выходные данные - нет. Я отправляю RInside команду 'sink (stderr (), type = "output") ", чтобы перенаправить stdout в stderr (так как stderr отображается в моей консоли), но обычный вывод по-прежнему не отображается. 'print (command)' работает, но я бы хотел более чистый способ передачи стандартного вывода прямо на консоль, как в обычной оболочке R.
Любая помощь и / или мысли будут высоко ценится. Дистиллированная версия моего кода показана ниже:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using namespace std;
string request_cpp;
ostringstream oss;
int read(FILE* tcp_fd)
{
/* function to read input from FILE* into the 'request_cpp' string */
}
int write(FILE* tcp_fd, const string& response)
{
/* function to write a string to FILE* */
}
int main(int argc, char* argv[])
{
// create RInside object
RInside R(argc,argv);
//socket
int sd = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(40650);
// set and accept connection on socket
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
bind(sd,(struct sockaddr*)&addr, sizeof(addr));
listen(sd,1);
int sd_i = accept(sd, 0, 0);
//re-route stdout and stderr to socket
close(1);
dup(sd_i);
close(2);
dup(sd_i);
// open read/write file descriptor to socket
FILE* fp = fdopen(sd_i,"r+");
// emulate R prompt
write(fp,"> ");
// (attempt to) redirect R's stdout to stderr
R.parseEvalQ("sink(stderr(),type=\"output\");");
// read from socket and pass commands to RInside
while( read(fp) )
{
try
{
// skip empty input
if(request_cpp == "")
{
write(fp, "> ");
continue;
}
else if(request_cpp == "q()")
{
break;
}
else
{
// clear string stream
oss.str("");
// wrap command in try
oss << "try(" << request_cpp << ");" << endl;
// send command
R.parseEvalQ(oss.str());
}
}
catch(exception e)
{
// print exception to console
write(fp, e.what());
}
write(fp, "> ");
}
fclose(fp);
close(sd_i);
exit(0);
}