Работая над встроенным проектом, я однажды попробовал работать на всем C, и просто не выдержал. Это было настолько многословно, что было трудно что-либо прочитать. Кроме того, мне понравились написанные мной оптимизированные для встраиваемых контейнеров, которые должны были превратиться в гораздо менее безопасные и более сложные для исправления #defineблоки.
Код, который на C ++ выглядел так:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
превращается в:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1)
что многие люди, вероятно, скажут, что это нормально, но становится нелепым, если вам нужно сделать больше, чем пару вызовов "методов" в строке. Две строки C ++ превратятся в пять строк C (из-за ограничений на длину строки в 80 символов). Оба будут генерировать один и тот же код, поэтому это не похоже на целевой процессор!
Однажды (еще в 1995 году) я попытался написать много C для многопроцессорной программы обработки данных. Такой, где у каждого процессора своя память и программа. Компилятор, предоставленный поставщиком, был компилятором C (своего рода производным от HighC), их библиотеки были с закрытым исходным кодом, поэтому я не мог использовать GCC для сборки, а их API-интерфейсы были разработаны с учетом того, что ваши программы будут в первую очередь инициализировать / процесс / terminate, поэтому взаимодействие между процессорами было в лучшем случае рудиментарным.
У меня было около месяца, прежде чем я сдался, нашел копию cfront и взломал ее в make-файлы, чтобы я мог использовать C ++. Cfront даже не поддерживал шаблоны, но код C ++ был намного понятнее.
Общие, типобезопасные структуры данных (с использованием шаблонов).
Самая близкая вещь C к шаблонам - это объявить файл заголовка с большим количеством кода, который выглядит так:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ }
затем вставьте что-то вроде:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Обратите внимание, что это не будет работать для составных типов (например, без очередей unsigned char), если вы не создадите typedefпервый.
Да, и помните, если этот код на самом деле нигде не используется, то вы даже не знаете, правильно ли он синтаксически.
РЕДАКТИРОВАТЬ: Еще одна вещь: вам нужно вручную управлять созданием кода. Если ваш "шаблонный" код - это не все встроенные функции, вам нужно будет ввести некоторый контроль, чтобы гарантировать, что все будет создано только один раз, чтобы ваш компоновщик не выплюнул кучу "нескольких экземпляров Foo". .
Для этого вам нужно будет поместить невстроенный материал в раздел «реализация» вашего файла заголовка:
#ifdef implementation_##TYPE
#endif
И затем, в одном месте всего вашего кода для каждого варианта шаблона , вы должны:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Кроме того, этот раздел реализации должен находиться за пределами стандартной #ifndef/ #define/ #endifлитании, потому что вы можете включить файл заголовка шаблона в другой файл заголовка, но после этого потребуется создать его экземпляр в .cфайле.
Да, быстро становится уродливо. Вот почему большинство программистов на C даже не пробуют.
RAII.
Особенно в функциях с несколькими точками возврата, например, когда не нужно забывать отпускать мьютекс в каждой точке возврата.
Что ж, забудьте свой красивый код и привыкните к тому, что все ваши точки возврата (кроме конца функции) будут 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;
}
Деструкторы в целом.
Т.е. вы пишете d'tor один раз для MyClass, тогда, если экземпляр MyClass является членом MyOtherClass, MyOtherClass не должен явно деинициализировать экземпляр MyClass - его d'tor вызывается автоматически.
Создание объекта должно выполняться точно так же.
Пространства имен.
На самом деле это просто исправить: просто прикрепите префикс к каждому символу. Это основная причина раздувания исходного кода, о котором я говорил ранее (поскольку классы являются неявными пространствами имен). Ребята из Си жили этим, ну, вечно, и, вероятно, не поймут, в чем дело.
YMMV