1

Тема: Exceptions in modern C++

Вітаю.
Виникло кілька питань, щодо використання вийняткових ситуацій в С++. А саме написання коду без використання throw та try/catch блоків. Як часто таке практикується в сучасному С++ у реальних проектах? Дуже часто пишуть, що exceptions не є дешевими і якщо важлива продуктивність, то краще використовувати if/else і передавати помилку через них. Чи дійсно це так і краще писати код не опираючись на exceptions?
А, як ви відноситесь до використання exceptions в С++?

2 Востаннє редагувалося koala (29.08.2022 13:16:37)

Re: Exceptions in modern C++

По-перше, дочасна оптимізація — корінь усього зла. Ваш метод з винятками знижує продуктивність? Що? Ви не робили профілювання? То зробіть, а потім думайте, як оптимізувати. Читаність важливіша.
По-друге, використовуйте винятки саме як винятки. Якщо ваш код реально очікує, що станеться виняток - маєте проблему; винятковий код має використовуватися рідко, як виняток.
По-третє, гляньте, як реалізована обробка помилок у Rust. У С++23 планується додати std::expected.

Подякували: mimik, leofun012

3 Востаннє редагувалося wander (29.08.2022 15:02:28)

Re: Exceptions in modern C++

mimik написав:

А, як ви відноситесь до використання exceptions в С++?

Відмінно ставлюся. Застосовувати треба вміти і все буде добре.

mimik написав:

[...] Написання коду без використання throw та try/catch блоків. Як часто таке практикується в сучасному С++ у реальних проектах?

Два випадки, коли відключення винятків виправдане:

  1. Середовище, в якому запускається програма, недружнє, або не підтримує виключення (див. сюди);

  2. Для програми потрібні гарантії безперервності у довільному місці.

У першому випадку код пишуть без винятків, бо їх неможливо підтримати. Отже, відключення генерації виключень супровідним кодом - це наслідок відсутності цієї підтримки. Навіщо роздувати код тим, що ніколи не використовуватиметься?
У другому випадку код спеціально пишуть так, щоб його переривання було неможливим. Отже, відключення генерації виключень супровідним кодом - це наслідок цієї вимоги. Навіщо роздувати код тим, що ніколи не використовуватиметься?

mimik написав:

Дуже часто пишуть, що exceptions не є дешевими і якщо важлива продуктивність, то краще використовувати if/else і передавати помилку через них.

Тут можна об'єктивно посперечатися.

// With exceptions
calculate():                          # @calculate()
        push    rax
        mov     rdi, qword ptr [rip + sz]
        call    allocate(unsigned long)
        mov     rsi, qword ptr [rip + sz]
        mov     rdi, rax
        pop     rax
        jmp     foo(int*, unsigned long)                       # TAILCALL

https://godbolt.org/z/jsaYxTP33

// Without exceptions
calculate():                          # @calculate()
        push    rax
        mov     rdi, qword ptr [rip + sz]
        call    allocate(unsigned long)
        test    rax, rax
        je      .LBB1_1
        mov     rsi, qword ptr [rip + sz]
        mov     rdi, rax
        pop     rax
        jmp     foo(int*, unsigned long)                       # TAILCALL
.LBB1_1:
        pop     rax
        ret

https://godbolt.org/z/8nczeK1G7
Там я навмисно відключив деякі оптимізації, щоб наблизити код до реальних умов.

Звичайно, сам процес розкручування стека не швидкий. Але при грамотному розподілі обробників (йдеться не про впихання try-catch в кожну функцію) конкретні функції виконуватимуться швидше в штатних ситуаціях.
У прикладі я зробив лише одну перевірку, а тепер уявіть, що таких перевірок там могла б бути сотня.

Якщо ми пишемо код із винятками і не намагаємося їх обробляти в стилі кодів повернення, то жодного уповільнення від їх роботи не буде або воно буде в межах похибки.

Подякували: mimik, leofun012

4

Re: Exceptions in modern C++

Зрозуміло, дякую. Але ж може бути і сотня catch-ів, що йдуть один за одним?
І для чого ось це потрібно std::size_t size = 2; // блокуємо оптимізацію на основі відомого значення?

5

Re: Exceptions in modern C++

mimik написав:

Але ж може бути і сотня catch-ів, що йдуть один за одним?

Якщо таке є, то хтось неправильно застосовує винятки :)
А, от сотні перевірок при прокиданні помилки нагору через if/else може і не вдасться позбутися. Тобто ще раз, exceptions – це виняткові ситуації. Винятковий – отже рідкісний. Якщо хтось на них в'яже регулярну логіку, він помиляється.

mimik написав:

І для чого ось це потрібно

std::size_t size = 2; // блокуємо оптимізацію на основі відомого значення

Змінна size глобальна, щоб компілятор не робив припущення, що size - константа і відповідні оптимізації.

Подякували: mimik1