Какие существуют методы для модульного кода C? - PullRequest
12 голосов
/ 22 июля 2009

Какие методы, практики и соглашения вы знаете для модульного кода C по мере роста проекта?

Ответы [ 8 ]

15 голосов
/ 22 июля 2009

Создание заголовочных файлов, которые содержат ТОЛЬКО то, что необходимо для использования модуля. В соответствующем файле (файлах) .c сделайте все, что не должно быть видимым снаружи (например, вспомогательные функции) статическим. Используйте префиксы для имен всего видимого извне, чтобы избежать коллизий пространства имен. (Если модуль охватывает несколько файлов, все становится сложнее. Вам может потребоваться раскрыть внутренние вещи и не иметь возможности скрыть их со «статическим»)

(Если бы я попытался улучшить C, я бы хотел сделать «статическую» область видимости функций по умолчанию. Если вы хотите, чтобы что-то было видно снаружи, вам нужно было бы пометить это как «export» или «global». или что-то подобное.)

10 голосов
/ 22 июля 2009

ОО-методы могут быть применены к коду C, они просто требуют большей дисциплины.

  • Используйте непрозрачные ручки для работы с объектами. Хорошим примером того, как это делается, является библиотека stdio - все организовано вокруг непрозрачного дескриптора FILE*. Многие успешные библиотеки организованы вокруг этого принципа (например, zlib , апр )
  • Поскольку все члены struct s неявно public в C, вам нужна дисциплина + программист для применения полезной техники сокрытия информации . Выберите простое, автоматически проверяемое соглашение, такое как «закрытые члены заканчиваются на« _ »».
  • Интерфейсы могут быть реализованы с использованием массивов указателей на функции. Конечно, это требует больше работы, чем в таких языках, как C ++, которые обеспечивают поддержку языка, но, тем не менее, это может быть сделано в C.
3 голосов
/ 22 июля 2009
  1. Не определять переменные в заголовочных файлах; вместо этого определите переменную в исходном файле и добавьте в заголовок оператор extern (объявление). Это будет связано с № 2 и № 3.
  2. Используйте защиту для каждого заголовка. Это избавит от многих головных болей.
  3. Предполагая, что вы сделали # 1 и # 2, включите все, что вам нужно (но только то, что вам нужно) для определенного файла в этом файле. Не зависит от того, как компилятор расширяет ваши директивы include.
3 голосов
/ 22 июля 2009

Статья High и Low-C C содержит много полезных советов. В частности, обратите внимание на раздел " Классы и объекты ".

Стандарты и стиль кодирования в ANSI C также содержит полезные советы, которые вы можете выбрать.

2 голосов
/ 22 июля 2009

Функция должна делать одну вещь и делать это хорошо.

Множество небольших функций, используемых большими функциями-обертками, помогают структурировать код из небольших, простых для понимания (и тестируемых!) Строительных блоков.

Создание небольших модулей с парой функций каждый. Выставляйте только то, что вы должны, сохраняйте все остальное внутри модуля. Связать небольшие модули вместе с их файлами интерфейса .h.

Предоставляет функции Getter и Setter для доступа к статическим переменным области файла в вашем модуле. Таким образом, переменные записываются только в одном месте. Это также помогает отслеживать доступ к этим статическим переменным, используя точку останова в функции и стек вызовов.

Одно важное правило при разработке модульного кода: не пытайтесь оптимизировать без необходимости. Множество небольших функций обычно дают более чистый, хорошо структурированный код, и дополнительные затраты на вызов функции могут стоить этого.

Я всегда стараюсь держать переменные в их самой узкой области, в том числе в функциях. Например, индексы для циклов обычно могут храниться в области видимости блока и не должны отображаться на уровне всей функции. C не так гибок, как C ++, с «определите, где вы его используете», но он работоспособен.

2 голосов
/ 22 июля 2009

Подход, который использует Pidgin (ранее Gaim), заключается в том, что они создали структуру Plugin. Каждый плагин заполняет структуру обратными вызовами для инициализации и разрыва, а также кучей другой описательной информации. Практически все, кроме структуры, объявлено как статическое, поэтому для связывания предоставляется только структура Plugin.

Затем, чтобы справиться со слабой связью плагина, связывающегося с остальной частью приложения (так как было бы хорошо, если бы он сделал что-то между настройкой и демонтажем), у них есть система сигнализации. Плагины могут регистрировать обратные вызовы для вызова, когда какая-либо часть приложения (включая другой плагин) выпускает определенные сигналы (не стандартные сигналы C, а настраиваемый расширяемый тип [идентифицируется строкой, а не заданными кодами]) Они также могут самостоятельно подавать сигналы.

На практике это работает хорошо - разные плагины могут быть связаны друг с другом, но связь довольно слабая - прямого вызова функций нет, все через сигнальную систему.

1 голос
/ 22 июля 2009

Разбиение кода на библиотеки связанных функций - это один из способов упорядочить вещи. Чтобы избежать конфликтов имен, вы также можете использовать префиксы, чтобы позволить вам повторно использовать имена функций, хотя с хорошими именами я никогда не находил, что это большая проблема. Например, если вы хотите разработать свои собственные математические подпрограммы, но при этом использовать некоторые из стандартной математической библиотеки, вы можете добавить к своей префиксу следующую строку: xyz_sin (), xyz_cos ().

Обычно я предпочитаю одну функцию (или набор тесно связанных функций) для каждого файла и один заголовочный файл для соглашения с исходным файлом. Разделение файлов на каталоги, где каждый каталог представляет отдельную библиотеку, также является хорошей идеей. Обычно у вас есть система make-файлов или файлов сборки, которая позволит вам собрать всю систему или ее часть в целом, следуя иерархии, представляющей различные библиотеки / программы.

0 голосов
/ 22 июля 2009

Есть каталоги и файлы, но нет пространств имен или инкапсуляции. Вы можете скомпилировать каждый модуль в отдельный файл obj и связать их вместе (как библиотеки).

...