Ah sim, é usado. Eu trabalho no campo do processamento de pacotes de rede. Eu tenho estado em duas empresas diferentes onde processamos pacotes de rede. Então, estamos operando no nível Ethernet ou IP, não no nível acima do TCP.
Curiosamente, nas duas empresas, o C foi escolhido em relação ao C ++. Em uma das empresas, um dos dois produtos foi construído sobre o kernel do Linux, enquanto o outro produto foi construído no espaço do usuário do Linux. O produto do kernel obviamente usou C como o kernel do Linux está programado em C, mas eles também escolheram usar o C para o produto do espaço do usuário. Ambos os produtos foram desenvolvidos a partir do ano 2000 (o produto kernel um pouco antes de 2000 e o produto userspace um pouco depois de 2000).
Na empresa em que fui depois disso, o produto foi construído em C, não em C ++. Na verdade, é uma continuação de um projeto de meados da década de 1990, embora devido a recentes demandas de melhoria de desempenho, foi decidido que essencialmente tudo será reescrito. Nós tivemos uma opção para selecionar C ++ devido a esta reescrita, mas não o fez.
No campo do processamento de pacotes de rede, o desempenho conta muito. Então, eu quero implementar minha própria tabela de hash com maior desempenho do que as tabelas de hash existentes. Eu, não o autor da tabela de hash, sou quem seleciona qual função hash deve ser usada. Talvez eu queira performance e escolha MurMurHash3 . Talvez eu queira segurança e vá para SipHash . Alocadores de memória são obviamente personalizados. Na verdade, todas as estruturas de dados importantes que usamos foram implementadas de forma personalizada para o melhor desempenho possível.
Embora não haja nada que impeça o uso de C ++, geralmente é uma má idéia. Uma única exceção lançada por pacote deixará a taxa de processamento de pacotes em níveis inaceitáveis! Portanto, não podemos usar as exceções do C ++. Muito devagar. Já estamos usando o tipo de código C orientado a objeto implementando estruturas de dados como estruturas e, em seguida, implementando funções operando nessas estruturas. C ++ permitiria ter funções virtuais, mas, novamente, as chamadas de função virtual matariam o desempenho se fossem usadas em todos os lugares. Portanto, é melhor ser explícito e ter um ponteiro de função se as chamadas de função virtual forem necessárias.
O C ++ fará muitas coisas pelas suas costas: alocação de memória, etc. Por outro lado, em C isso geralmente não acontece. Você pode escrever uma função que aloque memória, mas geralmente é aparente a partir da interface da função que a alocação está acontecendo.
Como exemplo do tipo de micro-otimizações que você pode fazer ao programar em C, dê uma olhada na macro container_of no kernel do Linux. Claro, você poderia usar container_of em código C ++, mas quem faz isso? Quer dizer, é totalmente aceitável na maioria dos programas em C, mas os programadores típicos de C ++ imediatamente propõem outra coisa, como uma lista encadeada que aloca os nós de link como blocos separados. Nós não queremos isso porque cada bloco de memória alocado é ruim para o desempenho.
Talvez a única coisa que nos beneficiaria em C ++ seja que o C ++ permita a metaprogramação de modelos, o que significa que você pode, às vezes, evitar chamadas de função virtual enquanto ainda tem um parâmetro de função e permitir que o compilador inline as funções. Mas a metaprogramação de template é complicada, e conseguimos atender a todos os requisitos em C, então o benefício desse recurso em C ++ não é tão crítico.
Em uma das empresas, tivemos uma linguagem compilada personalizada na qual parte dos recursos foram implementados. Adivinhe qual era o idioma de destino do compilador? Montagem? Não, nós tivemos que suportar arquiteturas de 32 e 64 bits. C ++? Certamente você está brincando. Obviamente, foi C com goto computado do GCC. Então, a linguagem customizada foi compilada para C (ou, na verdade, a variante gcc de C que suportava goto computado), e o compilador C produzia o assembly.