sergey_kovtunenko » 26 авг 2007, 15:44
Код: Выделить всё
/* main.c -- тестирующий библиотеку файл */
#include <stdio.h>
#include <setjmp.h>
#include "excpt.h"
void
foo(void);
void
func1(void);
void
foo(void)
{
printf("foo()\n");
// THROW(1);
printf("end foo()\n");
}
int
main(void)
{
printf("start main() : i=%d, j=%d\n", i, j);
/* TRY {
DBGTRYIN(main, 1);
TRY {
DBGTRYIN(main, 2);
THROW (100);
DBGTRYOUT(main, 2);
}
CATCH (100) {
DBGCATCHIN(main, 2, 100);
DBGCATCHOUT(main, 2, 100);
}
CATCHALL {
DBGCATCHALLIN(main, 2);
DBGCATCHALLOUT(main, 1);
}
TRYEND;
// THROW (10);
DBGTRYOUT(main, 1);
}
CATCH (10) {
DBGCATCHIN(main, 1, 10);
// THROW (5);
DBGCATCHOUT(main, 1, 10);
}
CATCHALL {
DBGCATCHALLIN(main, 1);
DBGCATCHALLOUT(main, 1);
}
TRYEND; */
func1();
printf("end main() : i=%d, j=%d\n", i, j);
return 0;
}
// Функция, в которой используются уже развёрнутые макроопределения
void
func1(void)
{
printf("start func1() : i=%d, j=%d\n", i, j);
do {
// j = i; // Это логическая ошибка
j++;
jbuf[j].ret = setjmp(jbuf[j].jbuf);
if (0 == jbuf[j].ret) {
{
DBGTRYIN(func1, 1);
// вложенный TRY
do {
j = i;
j++;
jbuf[j].ret = setjmp(jbuf[j].jbuf);
if (0 == jbuf[j].ret) {
{
DBGTRYIN(func1, 2);
longjmp(jbuf[j].jbuf, 10);
DBGTRYOUT(func1, 2);
}
// i = j; // если ты дошел до этой строчки, значит успешно (без THROW'ов, return'ов и raise()) выполнил TRY
} else if (10 == jbuf[j].ret) {
{
DBGCATCHIN(func1, 2, 10);
DBGCATCHOUT(func1, 2, 10);
}
// i = j;
// j = i;
} else {
{
DBGCATCHALLIN(func1, 2);
DBGCATCHALLOUT(func1, 2);
}
// i = j;
// j = i;
}
} while (0);
// конец вложенному TRY
longjmp(jbuf[j].jbuf, 100);
DBGTRYOUT(func1, 1);
}
i = j;
} else if (100 == jbuf[j].ret) {
{
DBGCATCHIN(func1, 1, 100);
DBGCATCHOUT(func1, 1, 100);
}
// i = j;
// j = i;
} else {
{
DBGCATCHALLIN(func1, 1);
DBGCATCHALLOUT(func1, 1);
}
// i = j;
// j = i;
}
} while (0);
printf("end func1() : i=%d, j=%d\n", i, j);
}
Для тех, кто дочитал до этого момента и не закрыл окно браузера, пояснение алгоритма и подводные камни.
Пояснение и различия с С++ подходом:
- Блоки обработчиков могут быть вложенными один в один. Аналогично с С++
- Если THROW(n) никто не обрабатывает, то программа вылетает с ошибкой. В либе реализуется за счёт передачи неверного значения в longjmp(), что крэшит программу на большинстве платформ. Полная аналогия с С++, за исключением невозможности (пока) зарегистрировать свой обработчик подобной ситуации.
- Если внутри блока выбрасывается THROW() исключение, то оно передаётся внешнему обработчику. Почти полная аналогия с С++, кроме того, что в либе передаётся аргумент, а в С++ тот же самый аргумент передаётся во внешний блок.
- Либа должна корректно обрабатывать ситуации, когда внутри блоков TRY\CATCH()\CATCHALL встречаются локальные переходы: goto\return()\raise(). И это одна из самых больших проблем при реализации алгоритма ;(((
В либе реализован массив значений {буффер_сохранения_состояния, код возвращаемого значения} размером в максимальную глубину вложенности. При заходе в новый блок TRY индекс массива увеличивается и запоминается новое состояние. При выходе из блока TRY индекс массива декрементируется.
Переменная j была нужна для защиты от локальных переходов и возбуждения сигналов goto\return()\raise(). Она работала так:
1. Сейчас мы находимся в i-том состоянии.
2. Делаем предположение, что наш блок TRY дойдёт нормально, без goto\return()\raise()\THROW() до самого конца. Для этого j = i+1;
3. Выполняем блок TRY.
3.1 Если блок TRY завершился успешно без исключений и переходов, то мы устойчиво стоим на новом состоянии, которое нужно "застолбить" в i, т.е. i=j
3.2 Если блок TRY завершился неудачей и исключением, то наша попытка окончилась неудачей, но мы всё равно остались стоять на i-том состоянии и переброска i = j не выполняется
Прошу помощи в реализации манипуляций с индексами массива и, возможно, логикой работы. Цель: сделать библиотеку "пуленепробиваемой" с некоторыми ограничениями.
[code]
/* main.c -- тестирующий библиотеку файл */
#include <stdio.h>
#include <setjmp.h>
#include "excpt.h"
void
foo(void);
void
func1(void);
void
foo(void)
{
printf("foo()\n");
// THROW(1);
printf("end foo()\n");
}
int
main(void)
{
printf("start main() : i=%d, j=%d\n", i, j);
/* TRY {
DBGTRYIN(main, 1);
TRY {
DBGTRYIN(main, 2);
THROW (100);
DBGTRYOUT(main, 2);
}
CATCH (100) {
DBGCATCHIN(main, 2, 100);
DBGCATCHOUT(main, 2, 100);
}
CATCHALL {
DBGCATCHALLIN(main, 2);
DBGCATCHALLOUT(main, 1);
}
TRYEND;
// THROW (10);
DBGTRYOUT(main, 1);
}
CATCH (10) {
DBGCATCHIN(main, 1, 10);
// THROW (5);
DBGCATCHOUT(main, 1, 10);
}
CATCHALL {
DBGCATCHALLIN(main, 1);
DBGCATCHALLOUT(main, 1);
}
TRYEND; */
func1();
printf("end main() : i=%d, j=%d\n", i, j);
return 0;
}
// Функция, в которой используются уже развёрнутые макроопределения
void
func1(void)
{
printf("start func1() : i=%d, j=%d\n", i, j);
do {
// j = i; // Это логическая ошибка
j++;
jbuf[j].ret = setjmp(jbuf[j].jbuf);
if (0 == jbuf[j].ret) {
{
DBGTRYIN(func1, 1);
// вложенный TRY
do {
j = i;
j++;
jbuf[j].ret = setjmp(jbuf[j].jbuf);
if (0 == jbuf[j].ret) {
{
DBGTRYIN(func1, 2);
longjmp(jbuf[j].jbuf, 10);
DBGTRYOUT(func1, 2);
}
// i = j; // если ты дошел до этой строчки, значит успешно (без THROW'ов, return'ов и raise()) выполнил TRY
} else if (10 == jbuf[j].ret) {
{
DBGCATCHIN(func1, 2, 10);
DBGCATCHOUT(func1, 2, 10);
}
// i = j;
// j = i;
} else {
{
DBGCATCHALLIN(func1, 2);
DBGCATCHALLOUT(func1, 2);
}
// i = j;
// j = i;
}
} while (0);
// конец вложенному TRY
longjmp(jbuf[j].jbuf, 100);
DBGTRYOUT(func1, 1);
}
i = j;
} else if (100 == jbuf[j].ret) {
{
DBGCATCHIN(func1, 1, 100);
DBGCATCHOUT(func1, 1, 100);
}
// i = j;
// j = i;
} else {
{
DBGCATCHALLIN(func1, 1);
DBGCATCHALLOUT(func1, 1);
}
// i = j;
// j = i;
}
} while (0);
printf("end func1() : i=%d, j=%d\n", i, j);
}
[/code]
[u][b]Для тех, кто дочитал до этого момента и не закрыл окно браузера, пояснение алгоритма и подводные камни.[/b][/u]
Пояснение и различия с С++ подходом:
[list]
[*] Блоки обработчиков могут быть вложенными один в один. [i]Аналогично с С++[/i]
[*] Если THROW(n) никто не обрабатывает, то программа вылетает с ошибкой. В либе реализуется за счёт передачи неверного значения в longjmp(), что крэшит программу на большинстве платформ. [i]Полная аналогия с С++, за исключением невозможности (пока) зарегистрировать свой обработчик подобной ситуации.[/i]
[*] Если внутри блока выбрасывается THROW() исключение, то оно передаётся внешнему обработчику. [i]Почти полная аналогия с С++, кроме того, что в либе передаётся аргумент, а в С++ тот же самый аргумент передаётся во внешний блок.[/i]
[*] [b]Либа должна корректно обрабатывать ситуации, когда внутри блоков TRY\CATCH()\CATCHALL встречаются локальные переходы: goto\return()\raise(). И это одна из самых больших проблем при реализации алгоритма ;((([/b][/list]
В либе реализован массив значений {буффер_сохранения_состояния, код возвращаемого значения} размером в максимальную глубину вложенности. При заходе в новый блок TRY индекс массива увеличивается и запоминается новое состояние. При выходе из блока TRY индекс массива декрементируется.
Переменная j была нужна для защиты от локальных переходов и возбуждения сигналов goto\return()\raise(). Она работала так:
[quote]1. Сейчас мы находимся в i-том состоянии.
2. Делаем предположение, что наш блок TRY дойдёт нормально, без goto\return()\raise()\THROW() до самого конца. Для этого j = i+1;
3. Выполняем блок TRY.
3.1 Если блок TRY завершился успешно без исключений и переходов, то мы устойчиво стоим на новом состоянии, которое нужно "застолбить" в i, т.е. i=j
3.2 Если блок TRY завершился неудачей и исключением, то наша попытка окончилась неудачей, но мы всё равно остались стоять на i-том состоянии и переброска i = j не выполняется[/quote]
[color=#FF0000][b]Прошу помощи в реализации манипуляций с индексами массива и, возможно, логикой работы. Цель: сделать библиотеку "пуленепробиваемой" с некоторыми ограничениями. [/b][/color]