Установка значения по умолчанию для bool

#c #multithreading #visual-c #boolean #critical-section

Вопрос:

Я работаю над классом очереди с использованием потоков в C (Windows 10 Visual Studio).

В моем конструкторе я начинаю с установки логического значения ошибки в значение false. Сразу после этого я пытаюсь инициализировать критическую секцию. Если есть проблема, bool имеет значение true, в противном случае он остается ложным. Я проверяю это значение в основной функции (не показано) и завершаю программу, если критическая секунда не инициализирована (значение bool равно false).

Код конструктора:

 Queue() {
        // ERROR_CRIT is bool: false = no error in initialising crit section, true = there is an error
        ERROR_CRIT = false;
        // check init 
        if (!InitializeCriticalSectionAndSpinCount(amp;CritSec, 0x00000400)) {
            ERROR_INIT_CRIT_SEC = true;
        }
        totalCustomers = 0;
        currentReadIndex = 0;
        currentWriteIndex = 0;
        customerNum = 0;
    };
 

Мой вопрос в следующем: какие типы логических значений я должен установить по умолчанию в значение true или false ? Я много раз думал об этом при написании других программ, и я не уверен, когда следует устанавливать значение по умолчанию для bool, если это не очевидно. Иногда кажется справедливым начать с любого значения. Я чувствую, что было бы странно, если бы код ошибки начинался как true, но его запуск как false также может быть странным.

В реальном мире или в компании я бы удалил строку 3 и добавил оператор else в if с else {ERROR_CRIT = false;} помощью ? Улучшит ли это читабельность ? Это, вероятно, предпочтение, но что часто встречается в программировании реального мира ?

Заранее благодарю вас 😀

Комментарии:

1. ERROR_INIT_CRIT_SEC = !InitializeCriticalSectionAndSpinCount(amp;CritSec, 0x00000400); ?

2. Я бы установил значение по умолчанию для bool, если предполагаемое значение переменной таково, что во время ее инициализации вы можете определить, какое значение она должна иметь. То же самое с любой переменной, на самом деле, а не только с bool. В этом случае ответ на вопрос «произошла ли ошибка при инициализации критического раздела?» в момент ERROR_CRIT инициализации это «нет», поэтому ERROR_CRIT по умолчанию false мне кажется разумным.

3. Замените все bool s, их настройку и тестирование на исключение. Почему — Queue объект находится не в хорошем состоянии, и это должно быть исключительной ошибкой.

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

5. Когда возникает исключение, вы не получаете объект. Вы не можете вызывать какие-либо методы тестирования, потому что у вас нет объекта. У вас либо есть отдельный путь кода, который позволяет обходиться без объекта, вы повторяете, пока не создадите объект успешно, либо вы зовете на помощь (сообщаете об ошибке) и прерываете программу. Часто вы будете использовать комбинацию 2 и 3: сделайте несколько повторных попыток, а затем сообщите и умрите.

Ответ №1:

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

Это делается путем создания исключения:

 Queue() {
    // check init 
    if (!InitializeCriticalSectionAndSpinCount(amp;CritSec, 0x00000400)) {
        throw std::runtime_error("failed to create the critical section");
    }
    totalCustomers = 0;
    currentReadIndex = 0;
    currentWriteIndex = 0;
    customerNum = 0;
};
 

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

Комментарии:

1. Ах, значит, исключение будет встроено в конструктор, а не отдельной функцией-членом ? Подвох здесь был бы в основном, и я мог бы решить, что тогда произойдет с программой ? Бросая, остальная часть конструктора будет проигнорирована правильно ? Только оператор if будет завершен, если произойдет бросок ?

2. @HusamChekfa Точно, создание исключения внутри конструктора приведет к прерыванию процесса создания объекта, а также любого кода, следующего за созданием объекта, до try тех пор, пока не будет выведен соответствующий блок. Как с этим справиться в main() зависимости от того, что вы хотите сделать, когда произойдет сбой. Если вы собирались выйти из программы, то вы можете просто оставить исключение без обработки, и это будет эквивалентно. В противном случае вам нужно перехватить/обработать исключение в соответствии с желаемым поведением.

3. Спасибо. Возможность полностью избежать создания bool и использовать вместо него метод броска идеально подходит

Ответ №2:

Хотя я в целом согласен с принятым ответом (и поддержал его), я хочу добавить, что устоявшийся подход в современной ОС заключается в том, что примитивы синхронизации в процессе работы не отказывают по причинам нехватки ресурсов или чего-то в этом роде.

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

Это подтверждается InitializeCriticalSectionAndSpinCount документацией:

Возвращаемое значение

Эта функция всегда выполняется успешно и возвращает ненулевое значение.

Windows Server 2003 и Windows XP: бла-бла-бла

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

C std::mutex и другие мьютексы C согласуются с этим и никогда не выбрасываются из-за нехватки памяти или ресурсов (в частности std::mutex , даже constexpr могут быть сконструированы, хотя Visual C нарушает это). Новые примитивы C 20 также не создают исключений.

Так что вы можете просто написать:

 Queue() noexcept {
    InitializeCriticalSectionAndSpinCount(amp;CritSec, 0x00000400);
    totalCustomers = 0;
    currentReadIndex = 0;
    currentWriteIndex = 0;
    customerNum = 0;
};
 

или, если вы параноик:

 Queue() noexcept {
    // check init 
    if (!InitializeCriticalSectionAndSpinCount(amp;CritSec, 0x00000400)) {
        __fastfail(1); // something terrible happened, cannot recover
    }
    totalCustomers = 0;
    currentReadIndex = 0;
    currentWriteIndex = 0;
    customerNum = 0;
};