Trabalhando em um projeto embarcado, tentei trabalhar em C uma vez, e simplesmente não aguentava. Era tão prolixo que tornava difícil ler qualquer coisa. Além disso, gostei dos contêineres otimizados para embutidos que escrevi, que tiveram que se tornar muito menos seguros e mais difíceis de corrigir #define
.
Código que em C ++ parecia:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
torna-se em:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1)
o que muitas pessoas provavelmente dirão que está bom, mas fica ridículo se você tiver que fazer mais do que algumas chamadas de "método" em uma linha. Duas linhas de C ++ se transformariam em cinco de C (devido aos limites de comprimento de linha de 80 caracteres). Ambos gerariam o mesmo código, então não é como se o processador de destino se importasse!
Uma vez (em 1995), tentei escrever muito C para um programa de processamento de dados com multiprocessador. O tipo em que cada processador tem sua própria memória e programa. O compilador fornecido pelo fornecedor era um compilador C (algum tipo de derivado do HighC), suas bibliotecas eram de código fechado, então eu não poderia usar o GCC para construir e suas APIs foram projetadas com a ideia de que seus programas seriam principalmente a inicialização / processo / terminate, portanto a comunicação entre processadores era, na melhor das hipóteses, rudimentar.
Eu demorei cerca de um mês antes de desistir, encontrei uma cópia do cfront e a hackeei nos makefiles para que eu pudesse usar C ++. Cfront nem mesmo suportava templates, mas o código C ++ era muito, muito mais claro.
Estruturas de dados genéricas e seguras de tipo (usando modelos).
A coisa mais próxima que C tem de modelos é declarar um arquivo de cabeçalho com uma grande quantidade de código semelhante a:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ }
em seguida, puxe-o com algo como:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Observe que isso não funcionará para tipos compostos (por exemplo, sem filas de unsigned char
) a menos que você faça um typedef
primeiro.
Ah, e lembre-se, se esse código não for realmente usado em nenhum lugar, você nem saberá se ele está sintaticamente correto.
EDIT: Mais uma coisa: você precisará gerenciar manualmente a instanciação do código. Se o seu código de "modelo" não for todas as funções embutidas, então você terá que colocar algum controle para se certificar de que as coisas sejam instanciadas apenas uma vez para que seu vinculador não cuspa uma pilha de erros de "várias instâncias de Foo" .
Para fazer isso, você terá que colocar o material não embutido em uma seção de "implementação" em seu arquivo de cabeçalho:
#ifdef implementation_##TYPE
#endif
E então, em um só lugar em todo o seu código por variante de modelo , você deve:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Além disso, esta seção de implementação precisa estar fora do padrão #ifndef
/ #define
/ #endif
litany, porque você pode incluir o arquivo de cabeçalho de modelo em outro arquivo de cabeçalho, mas precisa instanciar depois em um .c
arquivo.
Sim, fica feio rápido. É por isso que a maioria dos programadores C nem mesmo tenta.
RAII.
Especialmente em funções com vários pontos de retorno, por exemplo, não ter que se lembrar de liberar o mutex em cada ponto de retorno.
Bem, esqueça o seu código bonito e se acostume com todos os seus pontos de retorno (exceto o final da função) sendo goto
s:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
TYPE * result;
Mutex_Lock(this->lock);
if(this->head == this->tail)
{
result = 0;
goto Queue_##TYPE##_Top_exit:;
}
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
Destruidores em geral.
Ou seja, você escreve um d'tor uma vez para MyClass, se uma instância MyClass for membro de MyOtherClass, MyOtherClass não precisa desinicializar explicitamente a instância MyClass - seu d'tor é chamado automaticamente.
A construção de objetos deve ser explicitamente tratada da mesma maneira.
Namespaces.
Na verdade, isso é simples de corrigir: basta adicionar um prefixo a cada símbolo. Esta é a principal causa do inchaço da fonte de que falei anteriormente (uma vez que as classes são namespaces implícitos). O pessoal de C tem vivido isso, bem, desde sempre, e provavelmente não verá qual é o problema.
YMMV