О так, це використовується. Я працюю в галузі обробки мережевих пакетів. Я був у двох різних компаніях, де ми обробляємо мережеві пакети. Отже, ми працюємо на рівні Ethernet або IP, а не на рівні вище TCP.
Цікаво, що в обох компаніях C обирали над C ++. В одній з компаній один з двох продуктів був побудований на основі ядра Linux, тоді як інший продукт був побудований у просторі користувачів Linux. Продукт ядра, очевидно, що використовується C як ядро Linux, запрограмований на C, але вони вирішили використовувати C і для продукту простору користувачів. Обидва продукти були розроблені починаючи приблизно з 2000 року (продукт ядра трохи до 2000 року, а продукт простору користувача - трохи після 2000 року).
У компанії, куди я пішов після цього, продукт будувався на C, а не на C ++. Це фактично продовження проекту з середини 1990-х, хоча через останні вимоги до підвищення продуктивності було вирішено, що по суті все буде переписано. У нас була можливість вибрати C ++ завдяки цьому переписуванню, але цього не зробили.
У галузі обробки мережевих пакетів продуктивність налічує багато. Отже, я хочу реалізувати власну хеш-таблицю, що має більш високу продуктивність, ніж існуючі хеш-таблиці. Я, а не автор хеш-таблиці, я вибираю, яку хеш-функцію потрібно використовувати. Можливо, я хочу виступити і піти на MurMurHash3 . Можливо, я хочу забезпечити безпеку і поїхати за SipHash . Розподільники пам'яті, очевидно, на замовлення. Насправді всі важливі структури даних, які ми використовуємо, були реалізовані на замовлення для досягнення максимальної продуктивності.
Хоча немає нічого, що перешкоджало б використанню C ++, зазвичай це погана ідея. Один викинутий виняток за пакет знизить швидкість обробки пакету до неприйнятних рівнів! Отже, ми не можемо використовувати винятки C ++. Шлях занадто повільний. Ми вже використовуємо своєрідний об'єктно-орієнтований код С, реалізуючи структури даних як структури, а потім реалізуючи функції, що працюють на цих структурах. C ++ дозволить мати віртуальні функції, але потім знову виклики віртуальних функцій знищать продуктивність, якщо вони використовуються скрізь. Отже, краще бути явним і мати покажчик функції, якщо потрібні виклики віртуальних функцій.
C ++ зробить багато речей за вашою спиною: розподіл пам’яті тощо. З іншого боку, у C зазвичай цього не відбувається. Ви можете записати функцію, яка виділяє пам'ять, але зазвичай з інтерфейсу функції видно, що розподіл відбувається.
Як приклад типу мікрооптимізацій, які ви можете робити при програмуванні на C, подивіться на макрос контейнер_of у ядрі Linux. Звичайно, ви можете використовувати контейнер_of у коді C ++, але хто це робить? Я маю на увазі, це цілком прийнятно в більшості програм С, але типові програмісти C ++ негайно запропонують щось інше, наприклад, пов'язаний список, який виділяє вузли посилань як окремі блоки. Ми цього не хочемо, оскільки кожен виділений блок пам'яті поганий для продуктивності.
Мабуть, єдине, що допоможе нам у C ++, це те, що C ++ дозволяє метапрограмування шаблонів, тобто ви можете іноді уникати викликів віртуальних функцій, зберігаючи параметр функції, і дозволяти компілятору вбудовувати функції. Але метапрограмування шаблонів є складним, і нам вдалося виконати всі вимоги на C, тому користь цієї функції в C ++ не настільки важлива.
В одній із компаній у нас фактично була спеціальна мова для компіляції, де частина функцій була реалізована. Вгадайте, яка була цільова мова компілятора? Асамблея? Ні, нам довелося підтримувати як 32-бітну, так і 64-бітну архітектуру. C ++? Звичайно, ви жартуєте. Очевидно, це був C з обчисленим гото GCC . Так, спеціальна мова була компільована у C (або власне gcc-варіант C, який підтримував обчислені goto), і компілятор C виробляв збірку.