Trabajando en un proyecto incrustado, intenté trabajar en todo C una vez, y no pude soportarlo. Era tan detallado que hacía difícil leer cualquier cosa. Además, me gustaron los contenedores optimizados para incrustados que había escrito, que tenían que convertirse en #definebloques mucho menos seguros y más difíciles de arreglar .
Código que en C ++ se ve así:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
se convierte en:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1)
lo que mucha gente probablemente dirá que está bien, pero se vuelve ridículo si tiene que hacer más de un par de llamadas al "método" en una línea. Dos líneas de C ++ se convertirían en cinco de C (debido a los límites de longitud de línea de 80 caracteres). Ambos generarían el mismo código, por lo que no es como si el procesador de destino se preocupara.
Una vez (allá por 1995), intenté escribir mucho C para un programa de procesamiento de datos multiprocesador. Del tipo en el que cada procesador tiene su propia memoria y programa. El compilador proporcionado por el proveedor era un compilador de C (una especie de derivado de HighC), sus bibliotecas eran de código cerrado, por lo que no podía usar GCC para compilar, y sus API se diseñaron con la mentalidad de que sus programas serían principalmente el proceso de inicialización / terminar la variedad, por lo que la comunicación entre procesadores era rudimentaria en el mejor de los casos.
Llegué aproximadamente un mes antes de que me rindiera , encontré una copia de cfront y lo pirateé en los archivos MAKE para poder usar C ++. Cfront ni siquiera admitía plantillas, pero el código C ++ era mucho, mucho más claro.
Estructuras de datos genéricas con seguridad de tipos (utilizando plantillas).
Lo más parecido que tiene C a las plantillas es declarar un archivo de encabezado con mucho código que se parece a:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ }
luego tire de él con algo como:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Tenga en cuenta que esto no funcionará para tipos compuestos (por ejemplo, sin colas de unsigned char) a menos que realice una typedefprimera.
Ah, y recuerde, si este código no se usa en ningún lugar, entonces ni siquiera sabe si es sintácticamente correcto.
EDITAR: Una cosa más: deberá administrar manualmente la creación de instancias de código. Si su código de "plantilla" no son todas las funciones en línea, entonces tendrá que poner algo de control para asegurarse de que las cosas se instancian solo una vez para que su vinculador no escupe una pila de errores de "múltiples instancias de Foo" .
Para hacer esto, tendrá que poner las cosas que no están en línea en una sección de "implementación" en su archivo de encabezado:
#ifdef implementation_##TYPE
#endif
Y luego, en un lugar de todo su código por variante de plantilla , debe:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Además, esta sección aplicación necesita ser fuera de la norma #ifndef/ #define/ #endifletanía, ya que puede incluir el archivo de plantilla de encabezado en otro archivo de cabecera, pero necesario crear una instancia después en un .carchivo.
Sí, se pone feo rápidamente. Es por eso que la mayoría de los programadores de C ni siquiera lo intentan.
RAII.
Especialmente en funciones con múltiples puntos de retorno, por ejemplo, no tener que acordarse de liberar el mutex en cada punto de retorno.
Bueno, olvídate de tu bonito código y acostúmbrate a que todos tus puntos de retorno (excepto el final de la función) sean gotos:
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;
}
Destructores en general.
Es decir, escribe un d'tor una vez para MyClass, luego, si una instancia de MyClass es miembro de MyOtherClass, MyOtherClass no tiene que desinicializar explícitamente la instancia de MyClass; su d'tor se llama automáticamente.
La construcción de objetos debe manejarse explícitamente de la misma manera.
Espacios de nombres.
Eso es en realidad uno simple de arreglar: simplemente agregue un prefijo en cada símbolo. Esta es la causa principal de la hinchazón de la fuente de la que hablé anteriormente (ya que las clases son espacios de nombres implícitos). La gente de C ha estado viviendo esto, bueno, desde siempre, y probablemente no verán cuál es el problema.
YMMV