Ah, sim, é usado. Eu trabalho na área de processamento de pacotes de rede. Estive em duas empresas diferentes onde processamos pacotes de rede. Portanto, estamos operando no nível Ethernet ou IP, não no nível acima do TCP.
Curiosamente, nas duas empresas, C foi escolhido em vez de 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. Obviamente, o produto do kernel usado C como kernel do Linux é programado em C, mas eles 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, é a continuação de um projeto a partir de meados dos anos 90, embora, devido às recentes demandas de melhoria de desempenho, tenha sido decidido que basicamente tudo será reescrito. Tivemos a opção de selecionar C ++ devido a essa reescrita, mas não o fizemos.
No campo do processamento de pacotes de rede, o desempenho conta muito. Portanto, quero implementar minha própria tabela de hash com desempenho superior às tabelas de hash existentes. Eu, não o autor da tabela de hash, sou quem seleciona qual função de hash deve ser usada. Talvez eu queira desempenho e opte pelo MurMurHash3 . Talvez eu queira segurança e opte pelo SipHash . Alocadores de memória são obviamente personalizados. De fato, todas as estruturas de dados importantes que usamos foram implementadas de forma personalizada para obter o melhor desempenho possível.
Embora não exista nada que impeça o uso do C ++, geralmente é uma má idéia. Uma única exceção lançada por pacote reduzirá a taxa de processamento de pacotes para 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 objetos implementando estruturas de dados como estruturas e, em seguida, implementando funções que operam nessas estruturas. O C ++ permitiria ter funções virtuais, mas, novamente, as chamadas de função virtual reduziriam o desempenho se usadas em qualquer lugar. Portanto, é melhor ser explícito e ter um ponteiro de função se forem necessárias chamadas de função virtual.
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 aloca memória, mas geralmente é aparente na 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 no código C ++, mas quem faz isso? Quero dizer, é totalmente aceitável na maioria dos programas em C, mas os programadores típicos em C ++ propõem imediatamente outra coisa, como uma lista vinculada que aloca os nós do link como blocos separados. Não queremos isso porque cada bloco de memória alocado é ruim para o desempenho.
Talvez a única coisa que nos beneficiaria em C ++ é que o C ++ permite a metaprogramação de modelos, o que significa que às vezes você pode evitar chamadas de função virtual enquanto ainda possui um parâmetro de função e permitir que o compilador embuta as funções. Mas a metaprogramação de modelos é complicada e conseguimos atender a todos os requisitos em C; portanto, o benefício desse recurso em C ++ não é tão crítico.
Em uma das empresas, na verdade tínhamos uma linguagem compilada personalizada na qual parte dos recursos foram implementados. Adivinhe qual era o idioma de destino do compilador? Montagem? Não, tivemos que suportar arquiteturas de 32 e 64 bits. C ++? Certamente você está brincando. Obviamente, era C com o goto computado do GCC . Portanto, a linguagem personalizada foi compilada em C (ou na verdade a variante gcc de C que suportava goto computado), e o compilador C produziu o assembly.