LINUX.ORG.RU

А как вы бы определили что установлено 2 и более флага одновременно?

 


2

3

Есть int, в котором хранится значения битовых флагов:

enum Flags
{
   F1 = 1,
   F2 = 2,
   F3 = 4,
   F4 = 8
}

Как бы вы определили, что установлены 2 или более флага одновременно? Я сделал так:

if (signum(var & F1) +
    signum(var & F2) +
    signum(var & F3) +
    signum(var & F4) > 1)
   return 1
else
   return 0;
★★

Последнее исправление: cetjs2 (всего исправлений: 2)

Ответ на: комментарий от bogus_result

Если так, то я бы сделал для флагов пользовательские функции set_line_propertyname(on/off) а для исключающих флагов set_line_aliasmode(onlypoints/aliased/antialiased). Уже внутри можно, если хочется, паковать и свойства и режимы в один инт.

bogus_result
()

Есть простой способ проверить, что установлено больше одного флага.

return (x-1)&x;

Решение универсально и работает для любого количества возможных флагов.

Waterlaz ★★★★★
()
Ответ на: комментарий от Waterlaz

Ладно, похожие решения тут уже раз пять предложили =)

Waterlaz ★★★★★
()
Ответ на: комментарий от Waterlaz

Подобные решения выглядят красиво, но обладают одним недостатком - сначала нужно замаскировать все совместимые флаги, оставить только несовместимые. Значит для флага нужно как то хранить набор несовместимых с ним флагов. Если мы храним этот набор в виде маски, то перед установкой флага простое и с этой маской дает ответ - установлен ли уже один из несовместимых флагов. А если так, то и сбрасывать крайний правый бит никогда не понадобится.

bogus_result
()
Ответ на: комментарий от bogus_result

Те же рассуждения справедливы, если мы для флага не храним набор несовместимых флагов как маску, а жестко прописываем в коде как для него маскировать совместимые флаги.

bogus_result
()
Ответ на: комментарий от bogus_result

Извини, но я ничего не понял. Какие еще несовместимые флаги? Вопрос стоял, как определить, что установлено больше одного флага.

Waterlaz ★★★★★
()
double intpart;
int r;
r = modf( log(1.0l * (F1|F4)) / log(2.0l), &intpart ) > 0;

Дикий вариант через общеизвестную формулу, возвращающий 0, когда логическая сумма флагов является степенью двойки, т.е. когда в ней поднят только один флаг. Иначе возвращает 1.

(сам не сишник, поэтому не в курсе, есть ли там frac() попроще)

blexey ★★★★★
()
Ответ на: комментарий от Waterlaz

Не совсем так. Ранее по треду выяснилось, что флаги бывают совместимые (которые можно устанавливать и сбрасывать независимо друг от друга) и несовместимые (только один из них должен быть установлен). ТС вполне допускает, что клиентский код может выставить несколько несовместимых флагов,на этот случай и нужна такая проверка. Но проще и правильнее просто не позволять клиентскому коду выставлять более одного из несовместимых флагов (правильно организовав интерфейс или проверяя на этапе установки флага выставлен ли уже один несовместимый с ним), и тогда проверка установки нескольких несовместимых флагов не понадобится. Оттакая фигня.

bogus_result
()
Ответ на: комментарий от bogus_result

Но проще и правильнее просто не позволять клиентскому коду выставлять более одного из несовместимых флагов (правильно организовав интерфейс или проверяя на этапе установки флага выставлен ли уже один несовместимый с ним), и тогда проверка установки нескольких несовместимых флагов не понадобится. Оттакая фигня.

Waterlaz

Тут, кхм. Оттака фигня:

Хидер библиотеки:

enum SmlLStyles
{
    SML_LINE_ONLYPOINTS    = (0x000001),
    SML_LINE_ALIASED       = (0x000002),
    SML_LINE_ANTIALIASED   = (0x000004),
    SML_LINE_NOFIRSTPIXEL  = (0x000008),
    SML_LINE_NOLASTPIXEL   = (0x000010)
};

typedef uint32_t SmlLStyle;

SmlErrors SmlRasterDrawLineG (SmlIndex index, SmlLine line, SmlGradient grad, SmlLStyle lstyle);
SmlErrors SmlRasterDrawTetrGR(SmlIndex index, SmlTetragon tetragon, SmlTetragon corners, SmlGradient grad, SmlFStyle fstyle, SmlLStyle lstyle);


Клиентский код:
LOG(SmlRasterDrawTetrGR(img1, tetr, rou, grad, SML_FILL_EMPTY, SML_LINE_ANTIALIASED)); // Корректно

LOG(SmlRasterDrawTetrGR(img1, tetr, rou, grad, SML_FILL_EMPTY, SML_LINE_ANTIALIASED | SML_LINE_ALIASED)); // Некорректно, нельзя установить одновременно *данные* флаги

LOG(SmlRasterDrawLine(img1, line, SML_COLOUR_RED, SML_LINE_ANTIALIASED | SML_LINE_NOFIRSTPIXEL)); // Все ок

LOG(SmlRasterDrawLine(img1, line, SML_COLOUR_RED, SML_LINE_ONLYPOINTS | SML_LINE_ALIASED | SML_LINE_NOFIRSTPIXEL)); // Все плохо, нельзя одновременно ставить ONLYPOINTS и ALIASED


Проверки сейчас стоят так: (реализация DrawTetr)

SmlErrors SmlRasterDrawTetr(SmlIndex index, SmlTetragon tetragon, SmlColour colour, SmlFStyle fstyle, SmlLStyle lstyle)
{
    SML_CHECKELEM(index, SML_ELEM_IMAGE, SML_ERR_BADIMAGE);

    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ALIASED    )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ANTIALIASED)) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ONLYPOINTS ))) > 1)
        return SML_ERR_BADVALUE;

    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOFIRSTPIXEL )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOLASTPIXEL  ))) > 0)
        return SML_ERR_BADVALUE;

    switch (fstyle)
    {
    case SML_FILL_EMPTY  :  tetrempty (index, tetragon, colour, lstyle);  break;
    case SML_FILL_FILLED :  tetrfilled(index, tetragon, colour, lstyle);  break;
    default:
        return SML_ERR_BADVALUE;
    }

    return SML_ERR_SUCCESS;
}
sambist ★★
() автор топика
Ответ на: комментарий от bogus_result

А... ну тогда, действительно, проверять недопустимые комбинации по маске.

Waterlaz ★★★★★
()
Ответ на: комментарий от sambist
    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ALIASED    )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ANTIALIASED)) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ONLYPOINTS ))) > 1)
        return SML_ERR_BADVALUE;

    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOFIRSTPIXEL )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOLASTPIXEL  ))) > 0)
        return SML_ERR_BADVALUE;

можно заменить

#define GRP1 (SML_LINE_ALIASED | SML_LINE_ANTIALIASED | SML_LINE_ONLYPOINTS)
#define GRP2 (SML_LINE_NOFIRSTPIXEL | SML_LINE_NOLASTPIXEL)

if ( ((lstyle & GRP1) & ((lstyle & GRP1) - 1)) || (lstyle & GRP2) ) 
	return SML_ERR_BADVALUE;
anonymous
()
Ответ на: комментарий от sambist

Придумал.

Допустим, у нас 5 флагов. F1...F5. Причем, из F1 и F2 может быть выставлен тольк один и из F3, F4, F5 может быть выставлен только один.

Закодируем их так: F1=10000:01000, F2=01000:10000, F3=00001:00110, F4=00010:00101, F5=00100:00011. Двоеточия использую только для удобства восприятия.

Суммируется их как и прежде логическим «или». Пусть результат равен R=RL:RR. Двоеточие только для удобства.

Если из F1 и F2 выставлено больше одного, то RR & 11000 = 11000, а если из F3, F4, F5 выставлено больше одного, то RR & 00111 = 00111.

Проверка каждой группы несовместимых флагов займет одно действие.

bogus_result
()
Ответ на: комментарий от bogus_result

Можно обойтись и без RL и без левых частей флагов. Для каждого совместимого флага - он остается собой, каждый несовместимый заменяется его инверсией по группе несовместимых с ним. Метод работает, когда несовместимые флаги можно объединить в группы. Для более сложных правил совместимости не прокатит.

bogus_result
()
Ответ на: комментарий от sambist
typedef enum 
{
    SML_LINE_ONLYPOINTS,
    SML_LINE_ALIASED,
    SML_LINE_ANTIALIASED
} SmlLStyles;

typedef enum
{
    SML_LINE_E_NOFIRSTPIXEL,
    SML_LINE_E_NOLASTPIXEL
} SmlEndStyles;

SmlErrors SmlRasterDrawTetr(SmlIndex index, SmlTetragon tetragon, SmlColour colour, SmlFStyle fstyle, enum SmlLStyles lstyle, SmlEndStyles estyle);

int main()
{
    r = SmlRasterDrawTetr(0,0,0,0,SML_LINE_ONLYPOINTS,SML_LINE_E_NOFIRSTPIXEL);
}

К сожалению, в си enum никак не проверяются компилятором, так что тебе в функцию могут передать 100500 или перепутать какой стиль про что. Но по крайней мере так у тебя не будет необходимости проверять аргументы на противоречивость.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Вот такого подхода я стараюсь избежать. Потому что завтра у меня может появиться еще какая-нибудь сущность «в виде гномика». Имею в виду появилась сущность типа линии, появилась сущность индикации отрисовки первого\последнего пикселя (в основном нужно для построения других фигур, если хотите подробнее, могу рассказать), завтра появятся типы пунктира (давно хотел сделать, руки не доходят), мне еще один параметр вводить? Зачем, если я могу не меняя интерфейс функции и не ломая обратную совместимость дополнить функционал? (SML_LINE_ANTIALIASING | SML_LINE_NOFIRSTPIXEL | SML_LINE_DOTTED101) - Почему вы считаете такой интерфейс неудобным? У функции и так 5 «сложных» параметров. Если раскрыть там структуры, то параметров просто офигеть как много:

SmlErrors SmlRasterDrawTetrGR
(
  SmlIndex texture_index, 
  
  int32_t x1, 
  int32_t y1, 
  int32_t x2, 
  int32_t y2, 
  int32_t x3, 
  int32_t y3, 
  int32_t x4, 
  int32_t y4, 

  int32_t p1_rounded_ratio_x,
  int32_t p1_rounded_ratio_y,
  int32_t p2_rounded_ratio_x,
  int32_t p2_rounded_ratio_y,
  int32_t p3_rounded_ratio_x,
  int32_t p3_rounded_ratio_y,
  int32_t p4_rounded_ratio_x,
  int32_t p4_rounded_ratio_y,
 
  SmlIndex gradient_index, 
  SmlGradType gradient_type, 
  int32_t x1_gradient_line, 
  int32_t y1_gradient_line, 
  int32_t x2_gradient_line, 
  int32_t y2_gradient_line, 

  SmlColour colour, 

  SmlFStyle filling_style, 
  SmlLStyle line_style
);

И это просто вызов одной функции для рисования градиентного четырехугольника с закругленными углами.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

Сделай так:

#define grp aliased_bit|antialiased_bit|onlypoints_bit

#define aliased_flag grp^aliased_bit

#define antialiased_flag grp^antialiased_bit

#define onlypoints_flag grp^onlypoints_bit

#define other_flag other_bit

Собираем стиль так style=flag1|flag2|...

Метод проверки на непротиворечивость флагов такой

style&grp==grp? противоречивы : непротиворечивы

Нужно заменить задачу проверки того, что единиц в группе больше одной на задачу проверки того, что в группе все единицы.

bogus_result
()
Ответ на: комментарий от bogus_result

Такое же красивое решение существует и для более сложной задачи: есть две группы флагов, могут быть выставлены какие-то флаги первой группы, либо второй, но не обоих групп сразу. Проверить это.

bogus_result
()
Ответ на: комментарий от sambist

завтра появятся типы пунктира (давно хотел сделать, руки не доходят), мне еще один параметр вводить?

Конечно. Его так и так вводить, но это можно сделать явно, дописав аргумент в функцию, а можно скрытно, через флаги. Но неявные аргументы - это грабли.

Зачем, если я могу не меняя интерфейс функции и не ломая обратную совместимость дополнить функционал?

С каждым новым флагом придётся во все рисовальные функции дописывать всё усложняющиеся проверки на непротиворечивость. А вариант с раздельными enum просто не предусматривает возможности записать вызов функции неверно и проверки не нужны => код библиотеки меньше, ошибок меньше.

Обратную совместимость можно обеспечить через значения по умолчанию int f(x, y=0)

У функции и так 5 «сложных» параметров

Это с точки зрения компилятора их пять. А с точки зрения программиста их шесть, и планируется седьмой, и от этого не уйти. Если три параметра упаковать в один аргумент функции их семантически всё равно три, да ещё и правило упаковки добавляется. Сложность системы возрастает, а программист должен со сложностью бороться.

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от legolegs

Раз уж речь зашла о сложности, тут ее навалом.

Геометрические объекты, и расположение в системе координат, их цвет и модификаторы цвета, стиль их отрисовки и тд. все в одной куче.

Я бы предложил следующий механизм побороть сложность - рисование в несколько шагов: сначала генерируем полную сцену, потом ее отрисовываем.

Соответственно, на генерацию сцены вызовов функций можно будет не жалеть. Весь код упростится.

bogus_result
()
Ответ на: комментарий от legolegs

Обратную совместимость можно обеспечить через значения по умолчанию int f(x, y=0)

Нельзя. Смотрите теги.

А вариант с раздельными enum просто не предусматривает возможности записать вызов функции неверно и проверки не нужны => код библиотеки меньше, ошибок меньше.

Предусматривает, смотрите теги.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

Нельзя. Смотрите теги.

Принимается.

Предусматривает, смотрите теги.

Не принимается. Си, конечно, не запретит передать мусор вместо enum, но это верно и с вашим вариантом с, извините, кашей из битов. «проверки не нужны» говорилось про проверки на непротиворечивость флагов. Где-то всё равно будет case A:a();case B:b();default: return ERROR;

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Где-то всё равно будет case A:a();case B:b();default: return ERROR;

Ну и тогда какая разница? Сделаю я в каждую функцию ctrl+c/ctrl+v так:

    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ALIASED    )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ANTIALIASED)) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_ONLYPOINTS ))) > 1)
        return SML_ERR_BADVALUE;

// Это только для всех, кроме DrawLine и DrawCurve
    if ((SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOFIRSTPIXEL )) +
         SmlSgn(SML_FLAGISSET(lstyle, SML_LINE_NOLASTPIXEL  ))) > 0) 
        return SML_ERR_BADVALUE;

Или во всех функциях менять сигнатуры вызова, добавлять дополнительный switch-case, и что там еще.

sambist ★★
() автор топика
Последнее исправление: sambist (всего исправлений: 2)
Ответ на: комментарий от sambist

Нет, где-то будет происходить настоящая работа, как тут

    switch (fstyle)
    {
    case SML_FILL_EMPTY  :  tetrempty (index, tetragon, colour, lstyle);  break;
    case SML_FILL_FILLED :  tetrfilled(index, tetragon, colour, lstyle);  break;
    default:
        return SML_ERR_BADVALUE;
    }
и тут и будет сама собой происходить проверка на валидность енума. А отдельная проверка не нужна, это лишний код. но даже если вот прямо хочется всё заранее проверить, то енумы проверять проще, чем противоречивые флаги, просто
enum SmlLStyles
{
    SML_LINE_ONLYPOINTS,
    SML_LINE_ALIASED,
    SML_LINE_ANTIALIASED,
    SML_LINE_LASTOPTION=SML_LINE_ANTIALIASED
}
//...
    if (lstyle<0 || lstyle>SML_LINE_LASTOPTION)
        return SML_ERR_BADVALUE;

legolegs ★★★★★
()
Ответ на: комментарий от bogus_result

В си вот именно так нельзя. Ну и надо заводить объект «рисоватор», где-бы хранилось состояние, а потом его подчищать, в общем много возни ради синтаксического сахара.

legolegs ★★★★★
()
Ответ на: комментарий от bogus_result

Была мысль, но я нашел в таком подходе одну проблему - сейчас, с вызовом одиночной функции (функция в себе) я могу сделать так:

 - Create texture 1
 - Create texture 2

 - Start thread 1
 {
   Draw big figure on the texture 1 or read the instructions from file
 }
 - Start thread 2
 {
   Draw big figure on the texture 2 or read the instructions from file
 }

Т.е. такие вызовы параллелятся без всяких проблем (ну если рисуется на разных текстурах, тут уже никакую проверку на это кроме мьютексного блокирования не сделаешь, но это скорость замедлит когда все ок).

Что происходит, когда функция «разбита» на ваши вызовы:

OpenTexture(index1);
OpenTexture(index2);
MakeRect(a, b, c, d);
SetStyle(NO_FIRST);
MakeRect(f, g, h, i);
SetStyle(NORMAL);
SetMode(ALIASED);
SetMode(ANTIALIASED);
SetColour(RED);
SetGradient(gradindex):
Draw();
Draw();

Такие дела. Есть вариант с тем, что OpenTexture будет возвращать особый индекс, чтобы остальные его жрали, но это очередное усложнение, все равно придется иметь в библиотеке *динамический* массив, в котором будут храниться параметры для отрисовки #индекс, ибо они потом в Draw будут отрисовываться. А елозить malloc'ом на каждый вызов отрисовки (ну или через раз, если буферизировать) это как-то не комильфо.

sambist ★★
() автор топика
Ответ на: комментарий от legolegs

Немного с вами не согласен по поводу «автоматической проверки»:

enum SmlLStyles
{
    SML_LINE_ONLYPOINTS, // == 0
    SML_LINE_ALIASED,    // == 1
    SML_LINE_ANTIALIASED, // == 2
    SML_LINE_LASTOPTION=SML_LINE_ANTIALIASED // == 2 - согласны?
}
//...

Call(SML_LINE_ONLYPOINTS + SML_LINE_ALIASED)

    if (lstyle<0 || lstyle>SML_LINE_LASTOPTION)
        return SML_ERR_BADVALUE;

// Проверка пройдена, значение все равно не корректно.


sambist ★★
() автор топика
Ответ на: комментарий от sambist

Да, всё так. Энджой ёр Си. В документации на апи сказано передать enum, а юзер сложил какую-то фигню и получил не то, что хотел, но то что написал. Можно ещё вписать константу из другого енума[1]. Можно ещё не знать чем три режима отличаются и написать от балды. От этого защиты нет.

Мой пойнт в том, что чтобы совершать такую ошибку надо уж совсем ступить, ибо документация будет предельно проста: сказано enum такой-то, значит брать значения из него и точка. А в варианте с флагами всякие тонкости придётся разжёвывать. А и коде библиотеки тонкости имплементировать.

[1] - Кулстори: в jagged alliance 2 есть баг с характером персонажа из-за того, что игромеханические енумы были перепутаны с соответствующими им, но не равными гуёвыми

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 2)
Ответ на: комментарий от legolegs

В документации на апи сказано передать enum, а юзер сложил какую-то фигню и получил не то, что хотел, но то что написал. Можно ещё вписать константу из другого енума[1]. Можно ещё не знать чем три режима отличаются и написать от балды. От этого защиты нет.

Если так идти, то можно вообще никакой защиты не делать на одновременные флаги - типа написал ты ALIASED | ANTIALIASED, получил UB, а чего ты, собственно, хотел добиться? Можно опустить все проверки на наличие элемента в хранилище:

LOG(SmlFontCreate(&fnt1, "Ubuntu", SML_FONT_REGULAR));
LOG(SmlGradientSetColour(>>> fnt1 <<<,  SML_GRAD_MAX * 1.0,  0x0000FF)); // Несовместимая функция

SmlIndex win1;
LOG(SmlWindowVisibleSet(win1, SML_VISIBLE)); // Не было вызова WindowCreate

Но я делаю такие защиты по мере сил, несмотря на то, что они у меня занимают эдак 25% времени исполнения в отдельных случаях (ну не нашел я более оптимального способа поиска элемента в хранилище кроме как банального цикла).

Вот только издержки такого подхода вы уже описали в [1].

Была мысль сделать две верлии библиотеки - debug и release:

SmlErrors SmlButtonPositionGet(SmlIndex index, SmlPoint * pos)
{
#ifdef DEBUG
    SML_CHECKELEM(index, SML_ELEM_BUTTON, SML_ERR_BADBUTTON);

    SML_CHECKPTR(pos);
#endif // DEBUG

#ifdef DEBUG    
SML_CHECKLOC(
#endif // DEBUG
             SmlWindowPositionGet(warehouse.elem[index].data.btn.window, pos)
#ifdef DEBUG
            );
#endif // DEBUG

    return SML_ERR_SUCCESS;
}

Вот только код становится похож на новогоднюю елку и все равно, если идти идеальным путем и сначала написать проект на debug версии библиотеки, а потом подложить release, да, будет прирост скороси в 25+%, вот только где гарантия что все юнит-тесты были прогнаны правильно, что все были проверены результаты выполнения всех функций? Сейчас же как пишут люди, воспитанные этими вашими плюсами?

int func(int x)
{
   if (x > 5) return x;
         else return 0;
}

////

func(5);
func(6);
func(-1);

Без всяких проверок результата. «Если функция возвращает код ошибки он должен быть проверен» - когда вы в последний раз сталкивались с соблюдением этого правила?

sambist ★★
() автор топика
Последнее исправление: sambist (всего исправлений: 1)
Ответ на: комментарий от sambist

Ну разумеется, на таком языке как си приходится доверять здравому смыслу пользователя библиотеки. До каких пределов доверять - это уже революционно-пролетарское чутьё подскажет. Идеальных сишных библиотек не бывает и быть не может, всегда найдётся способ прострелить себе ногу.

Изначально я всего лишь указал, что изобретать для выражения режимов несовместиные между собой флаги с проверками на непротиворечивость - усложнение, а не упрощение.

legolegs ★★★★★
()
Ответ на: комментарий от sambist

Вообще, можно и без новогодней елки. Сделай SML_CHECK* макросами возможно с переменным количеством параметров, которые в случае ifdef DEBUG делают реальную работу, а в противном случае не делают ничего.

А насчет правила про код ошибки - я слышал, что не все так однозначно.

bogus_result
()
Ответ на: комментарий от bogus_result

SML_CHECK* макросами

Ну они как-бы и так макросы. В принципе очень неплохая мысль. Спасибо, я над ней подумаю.

А насчет правила про код ошибки - я слышал, что не все так однозначно.

Можете развить мысль?

sambist ★★
() автор топика
Последнее исправление: sambist (всего исправлений: 1)
Ответ на: комментарий от sambist

Есть мнение, что возвращать код ошибки - плохая практика, т.к. его легко игнорировать. Ну а для ошибок надо кидать исключения.

anonymous
()
Ответ на: комментарий от anonymous

В который раз повторяю: смотри теги.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

Могу развить😊

Код ошибки, если он возвращается, конечно надо обрабатывать, о чем речь. Тут не спорю.

Но надо ли его возвращать, и если надо, то в каких случаях? То же относится и к исключениям, но не суть.

Анализируя код старых BSD систем, Plan9, MIT Scheme, и многих других, можно сделать вывод: двумя крайностями были стили обработки ошибок Беркли и МИТ. В первом библиотечные функции полагались на то, что им передадут корректные параметры. Во втором библиотечные функции параноидально проверяли все, что им передавали при вызове, и возвращали коды ошибки. Оба случая это крайности. Нам нужна выгода от их смешивания.

Достичь этой выгоды помогает контракт. Контракт это набор ограничений, которым должны удовлетворять передаваемые параметры и возвращаемое значения, если таковые есть. Нарушение контракта есть ошибка именно программиста, и оно должно сразу валить все приложение дабы быть обнаруженным как можно раньше. Я предпочитаю ассерты для проверки контрактов. Пусть умники, которые при этих словах начали плеваться, представят себе пол гига только исходных кодов, четверть из которых унаследованный код. И, например, неожиданный NULL из очереди сообщений приходит. А от кого он, ещё надо раскрутить и понять. В бытность студентотой я много времени провел только в отладчике.

Да, так вот, ассерты. А что же насчет некорректных данных, ввведенных от пользователя? Тут нужен защитный контур. Функции контура проверяют введенные данные и возвращают пользователю ошибки, внутрь контура попадают только корректные данные и внутри контура считается, что нарушение контракта это нонсенс.

Для системных ресурсов также делаем отдельный контур безопасности. Функции этого контура - максимально точно указать детали сбоя и максимально быстро свалить приложение.

А что же коды ошибок? Я считаю, что возвращать их нужно только тогда, когда реально что то сделать с ошибкой - восстановить после неё или исправить и попробовать снова. Но ведь это может быть сделано зачастую на несколько уровней выше, чем ошибка произошла. Тут тоже есть сложности.

bogus_result
()
Ответ на: комментарий от bogus_result

Может быть, слишком упрощенно написал, но не суть.

Код ошибки, короче, не только должен быть обработан, еще его должно быть можно адекватно обработать (это зачастую означает, что должна быть завязка на уровень, где его можно обрабртать), должен быть смысл его обрабатывать, и должно быть выгоднее его обрабатывать чем валить приложение на фиг.

bogus_result
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.