Вопрос по namespaces, encapsulation, c, module, scoping – Какие существуют методы для модульного кода C?

12

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

+1 Хороший вопрос, думал о том же сегодня утром. helpermethod

Ваш Ответ

8   ответов
2

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

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

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

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

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

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

3

Высокий и низкий уровень C Статья содержит много полезных советов. Особенно обратите внимание на & quot;Classes and objects& Quot; раздел.

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

+1 за статьи!
2

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

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

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

+1. Это очень расширяемый способ проектирования системы. Интерфейсы (таблицы обратных вызовов) сводят связь к минимуму, облегчая замену в новых / улучшенных реализациях для различных компонентовseparately в будущем.
15

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

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

может быть, это только для переменных, а не для функций ...
В основном я имел в виду использование static с прототипами функций. Библиотека должна иметь как можно меньше своих собственных данных, помещая их вместо этого в клиент библиотеки, если только для этого нет веской причины (например, очередь оборудования, которая может иметь только одного потребителя, и ваша библиотека - это она). и распространяет данные своим клиентам, или что-то в этом роде.)
Убедитесь, что заголовок работает изолированно, используя его в качестве первого заголовка, указанного в файле реализации. Если это не работает, заголовок является неполным.
Я думал статичноwas область по умолчанию. По крайней мере, это то, что я помню, читая в k & amp; r.
+1. Использование статических функций для & quot; приватного & quot; поведение ограничивает возможность ненужного соединения.
10

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

Use opaque handles to operate on objects. One good example of how this is done is the stdio library -- everything is organised around the opaque FILE* handle. Many successful libraries are organised around this principle (e.g. zlib, apr) Because all members of structs are implicitly public in C, you need a convention + programmer discipline to enforce the useful technique of information hiding. Pick a simple, automatically checkable convention such as "private members end with '_'". Interfaces can be implemented using arrays of pointers to functions. Certainly this requires more work than in languages like C++ that provide in-language support, but it can nevertheless be done in C.
непрозрачные ручки это хорошо. И хотя структуры безоговорочно публичны, только если вы поместите их в заголовок. Вы можете сказать, в пользовательском заголовке для библиотеки, например: [code] struct opaque_foo; extern int my_func (struct opaque_foo * f); [/ code] Внутренний код библиотеки, конечно, будет содержать описание того, что находится в opaque_foo, но нет необходимости показывать это коду, который использует библиотеку.
1

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

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

+1, но, пожалуйста, используйте один Makefile, который рекурсивно #include Makefile.inc файлы в подкаталогах, так как традиционный рекурсивный make-файл может незаметно нарушать зависимости сборки (google & quot; Рекурсивные Makefile-файлы считаются вредными & quot;)
0

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

DLL - это тип библиотеки.
а как насчет создания DLL, содержащих соответствующие функции?
3
Don't define variables in header files; instead, define the variable in the source file and add an extern statement (declaration) in the header. This will tie into #2 and #3. Use an include guard on every header. This will save so many headaches. Assuming you've done #1 and #2, include everything you need (but only what you need) for a certain file in that file. Don't depend on the order of how the compiler expands your include directives.
В приложении к книге Эндрю Гласснера "Graphics Gems" есть изящный трюк. в котором у него есть несколько макросов, которые позволяют вам определять как объявление переменной, так и ее внешнее объявление (и инициализатор) в одной строке кода в заголовке. Это позволит файлу реализации * .c использовать тот же заголовок, что и код клиента. Пример из некоторого моего кода:gneutronica.cvs.sourceforge.net/viewvc/gneutronica/gneutronica/… посмотрите на макросы GLOBAL и INIT, и как они определяются условно.
+1 для # включенных охранников.

Похожие вопросы