Когда мне приходилось серьезно писать код C, мне приходилось имитировать в нем возможности C ++. Главное, что стоит сделать:
Думайте о каждом модуле как о классе. Функции, которые вы показываете в заголовке, похожи на общедоступные методы. Добавляйте функцию в заголовок, только если она является частью необходимого интерфейса модуля.
Избегайте циклических зависимостей модулей. Модуль A и модуль B не должны вызывать друг друга. Вы можете реорганизовать что-то в модуль C, чтобы этого избежать.
Опять же, следуя шаблону C ++, если у вас есть модуль, который может выполнять одни и те же операции с разными экземплярами данных, имейте в вашем интерфейсе функцию создания и удаления, которая будет возвращать указатель на структуру, который передается обратно другим функциям. . Но для инкапсуляции верните указатель void в общедоступном интерфейсе и приведите его к своей структуре внутри модуля.
Избегайте переменных в области видимости модуля - описанный ранее шаблон обычно делает то, что вам нужно. Но если вам действительно нужны переменные области видимости модуля, сгруппируйте их в структуру, хранящуюся в одной переменной области видимости модуля, называемой «m» или чем-то подобным. Затем в вашем коде всякий раз, когда вы видите "m.переменная "вы сразу узнаете, что это одна из структур области модуля.
Чтобы избежать проблем с заголовком, поместите #ifndef MY_HEADER_H #define MY_HEADER_H объявление, которое защищает от двойного включения. Заголовочный файл .h для вашего модуля должен содержат только #includes, необходимые ДЛЯ ЭТОГО ФАЙЛА ЗАГОЛОВКИ. Файл .c модуля может иметь больше включений, необходимых для компиляции модуля, но не добавляйте их в файл заголовка модуля. Это избавит вас от множества конфликтов пространств имен и проблемы порядка включения.
Истины об архитектуре систем неподвластны времени, и они пересекают языковые границы. Вот небольшой совет для языка C:
Каждый модуль скрывает секрет. Стройте свою систему вокруг интерфейсов, которые скрывают информацию от своих клиентов. Единственная безопасная для типов конструкция Си, скрывающая информацию, - это указатель на неполную структуру. Изучите ее досконально и используйте часто.
Один интерфейс на одну реализацию - это хорошее эмпирическое правило. (Интерфейс - это .h, реализация - .c.) Иногда вы захотите предоставить два интерфейса, относящихся к одной и той же реализации: один, скрывающий представление, и другой, раскрывающий его.
Вам понадобятся соглашения об именовании.
Превосходной моделью того, как решать подобные проблемы на языке Си, является книга Дейва Хэнсона C Interfaces and Implementations. В ней вы увидите, как разрабатывать хорошие интерфейсы и реализации, и как один интерфейс может опираться на другой, образуя целостную библиотеку. Вы также получите отличный набор стартовых интерфейсов, которые вы сможете использовать в своих собственных приложениях. Для человека в вашем положении, я не могу рекомендовать эту книгу слишком высоко. Она является архетипом хорошо спроектированных систем на C.
Чистота пространства имен - особенно важна для библиотек. Префикс ваших публичных функций должен содержать название библиотеки, каким-либо образом. Если ваша библиотека называется 'happyland', создайте такие функции, как "happyland_init" или даже "hl_init".
Это относится и к использованию статики. Вы пишете специализированные функции - спрячьте их от других модулей, широко используя static.
Для библиотек реентерабельность также очень важна. Не зависьте от глобального состояния, которое не отделено (при необходимости вы можете иметь typedef struct token для хранения этого состояния).
Это несколько моих известных советов, которые я могу вам дать.
Как создать интерфейс, который способствует сохранению кода и является расширяемым для будущих обновлений.
Раскрывая как можно меньше деталей реализации. Например,
static
, и не помещайте ее в .h-файл.