Пока интерпретирую и транспилирую Brainfuck, у меня есть серия обращений к printf()/puts():
while (ip < ops->count) {
Op op = ops->items[ip];
switch (op.kind) {
case '+':
printf(" t.mem[t.head] += %zu;\n", op.operand);
++ip;
break;
case '-':
printf(" t.mem[t.head] -= %zu;\n", op.operand);
++ip;
break;
case '.':
printf(" xputchar_many(&t, %zu);\n", op.operand);
++ip;
break;
case ',':
printf(" xgetchar_many(&t, %zu);{\n", op.operand);
++ip;
break;
case '>':
printf(" t.head += %zu;\n\n"
" grow_tape_and_append_many_zeroes(&t);\n", op.operand);
++ip;
break;
case '<':
printf(" t.head = t.head < %zu ? t.count - (%zu - t.head) : t.head - %zu;\n",
op.operand, op.operand, op.operand);
++ip;
break;
case '[':
puts(" while (t.mem[t.head] != 0) {");
++ip;
break;
case ']':
puts(" }");
++ip;
break;
}
}
Вызовы puts()/printf() генерируют код.
У меня есть два вопроса:
Есть ли шанс на восстановление после сбоя printf() (т. е. возврата отрицательного целого числа) или сбоя puts() (т. е. возврата EOF)? (Я придерживаюсь мнения, что нет.)
Если нет, то полезен ли вызов fprintf(stderr, ...) перед выходом? (Я считаю, что да.)
Is there any chance of recovery after printf() returns a negative value?" что ты имеешь в виду?
@gulpr Я обновил вопрос. Это все еще неоднозначно?
Если да, то ваша система или программа мертва и восстановление невозможно :)
Это в контексте, когда стандартный вывод перенаправляется в какую-то другую форму вывода, например в файл или последовательный порт? Иначе я не понимаю проблемы.
@Lundin printf()/puts() можно заменить на fprintf()/fputs() и вывести в какой-нибудь файл. stdout можно перенаправить. Проблема заключается в неправильно сгенерированном коде или наполовину сгенерированном коде без индикации ошибки.
Я не понимаю значения примера кода; Я предположил, что это не имеет значения, и ответил буквально на вопрос. Если вы считаете, что ваш код и xputchar_many в чем-то важны, уточните.
@anatolyg Звонки на puts()/printf() были важной частью. Пример кода был добавлен, потому что есть разница в генерации кода с помощью printf() и выводе "Hello World." на консоль (для чего я бы не стал проверять возвращаемое значение). Хотя я удалил часть, говорящую о xputchar_many(). Спасибо.





В принципе да. В заголовочном файле <stdio.h> есть специальная функция clearerr, предназначенная именно для этого. Вы можете попробовать отобразить ошибку, используя perror, попробуйте сделать clearerr и повторите попытку.
Возможный вариант использования, предложенный Эриком Постпишилом:
Вы пишете логи на какое-то устройство. Он заполняется, а
putsвозвращает ошибку из-за нехватки места. Вы [...] удаляете старые файлы журналов и повторяете попытку.
На сайте cppreference приведен слегка запутанный пример ошибки, которая мне кажется исправимой (я не пробовал):
setlocale(LC_ALL, "en_US.utf8");
errno = 0;
if (fputwc(L'🍌', stdout) == WEOF)
{
if (errno == EILSEQ)
puts("Encoding error in fputwc.");
else
puts("I/O error in fputwc.");
return EXIT_FAILURE;
}
Если в stdout есть ошибки, stderr все равно можно будет записать! Распространенным примером является перенаправление (вы можете перенаправить stdout с помощью оболочки или freopen, оставив stderr подключенным к консоли).
Вместо puts() в примере кода рассмотрите perror();.
Быть «Но я не могу представить ситуацию, когда это может быть полезно на практике»: Вы пишете логи на какое-то устройство. Он заполняется, а puts возвращает ошибку из-за нехватки места. Вы ищете и удаляете старые файлы журналов и повторяете попытку.
Есть ли шанс на восстановление после сбоя
printf()(т. е. возврата отрицательного целого числа) или сбояputs()(т. е. возвратаEOF)? (Я придерживаюсь мнения, что нет.)
Вряд ли программа сможет что-либо сделать для восстановления сама по себе. Как указывает другой ответ, вы можете очистить индикатор ошибки пункта назначения FILE и повторить попытку, но нет особых оснований ожидать, что этого будет достаточно (но также нет причин предполагать, что этого может быть недостаточно в любом случае). конкретный экземпляр).
Однако вы, вероятно, задаете неправильный вопрос. Вы пишете транспилятор Brainfuck. Первые два вопроса, которые вам следует задать здесь:
Стоит ли пытаться восстановиться после ошибок ввода-вывода?
и
Если нет, то что следует сделать вместо этого?
Я утверждаю, что нет, для этой утилиты не стоит тратить время и силы на механизм восстановления ошибок ввода-вывода. Такие ошибки вряд ли возникнут и в любом случае вряд ли будут иметь хорошие варианты восстановления, а роль утилиты не является критической или чувствительной. Стратегия восстановления таких ошибок, скорее всего, окажется напрасной.
Вместо этого программа, вероятно, должна просто попытаться распечатать диагностику stderr и выйти со статусом сбоя или, может быть, даже abort().
Вы написали в комментариях:
Проблема заключается в неправильно сгенерированном коде или наполовину сгенерированном коде без индикации ошибки.
Либо выход со статусом сбоя, либо завершение из-за сигнала (который будет результатом вызова abort()) можно обнаружить в среде, из которой запускается программа, поэтому любой из этих вариантов обеспечивает индикацию ошибки, которая может предупредить о сгенерированном коде. быть неисправным в результате той ошибки, о которой вы спрашиваете.
Могут быть и другие признаки, которые программа может предоставить. Например, как отметил @JonathanLeffler в комментариях, если выходные данные будут направлены в обычный файл, путь к которому известен программе, то она может попытаться удалить этот файл при завершении из-за ошибки ввода-вывода. Желательно ли то или иное подобное поведение, решать вам.
Если выходные данные записывались в файл (а не в стандартный вывод), выходной файл может быть удален при возникновении ошибки — и отсутствие выходного файла также является неоспоримым свидетельством того, что возникла проблема. OTOH, это может испортить некоторые доказательства того, что пошло не так.
Согласен, @JonathanLeffler, хотя, конечно, удаление тоже может не удаться. Например, если проблема возникла в первую очередь из-за того, что файловая система была размонтирована или перемонтирована только для чтения. Я бы охарактеризовал удаление выходного файла как возможное дополнительное поведение в режиме сбоя, доступное, когда местом назначения был файл, путь к которому был известен программе.
Да, мне следует переместить
++ipв конецswitchутверждения.