При реализации модуля на чистом C, который имеет более одного интерфейса и разделен на «подмодули», как лучше всего обрабатывать внутренние («частные») интерфейсы по сравнению с интерфейсами API? (Очевидно, что в C нет функции Private/protected как таковой, но есть способы сделать что-то.)
Пример:
my_module имеет два интерфейса: один для кода нижнего уровня (например, HAL), а другой для приложения. Итак, в папке включения у меня есть:
my_module_HAL.h
my_module_API.h
и в исходной папке есть соответствующие файлы .c
Однако внутри функции, реализованные в my_module_XXX.c
, должны вызывать друг друга. Как лучше всего это сделать? Я вижу несколько способов, какие плюсы и минусы у каждого?
_my_module_HAL.h
или каким-либо другим соглашением об именахЕсть ли веская причина предпочесть одно другому? Есть ли другие альтернативы, которые лучше? Обратите внимание, что мы также хотим сделать модульное тестирование кода максимально простым и чистым, используя ceedling.
Обновлено:
Я выбрал первый подход, и на самом деле он работает довольно хорошо. Структура кода такая:
SRC directory:
- sub_module_a.c
- _sub_module_a.h
_ sub_module_b.c
- _sub_module_b.h
- _sub_module_c.c
- _sub_module_c.h
INC directory:
- sub_module_a.h
- sub_module_b.h
На самом деле все немного сложнее: общедоступные заголовки в INC разделены на части для уровней HAL и APP, которые используют модуль с совершенно разными требованиями и интерфейсами. _sub_module.c — полностью внутренняя функция без открытого интерфейса (например, для проверки CRC),
Мы используем модульное тестирование с фреймворком ceedling, и эта структура, похоже, вполне хорошо с этим работает. У нас хорошая модульность кода и по названиям файлов видно, что они делают и куда к ним следует обращаться. Очень доволен этим решением.
Я бы сохранил частные файлы заголовков вместе с файлами .c для модуля и поместил общедоступные заголовки в каталог включения. Также старайтесь избегать загрязнения пространства имен во внешне связанных частных функциях.
@danmcb. Здесь 5 вопросов (некоторые весьма субъективны). Рассмотрите возможность сокращения набора вопросов.
Как лучше всего это сделать? Я вижу несколько способов, какие плюсы и минусы у каждого?
иметь больше файлов .h в исходной папке, называемых примерно так
_my_module_HAL.h
или каким-либо другим соглашением об именах используйте ключевое слово extern в исходных файлах
Похоже, вы спрашиваете, использовать ли такой код:
#include "my_module_HAL_internal.h"
или вот так:
extern void foo_internal(…);
extern void bar_internal(…);
…
где my_module_HAL_interal.h
будет содержать эти extern
объявления.
В этом случае ответ ясен: используйте заголовок. Это пример СУХОГО: не повторяйте себя. При использовании метода «Запись объявлений в исходные файлы» вам придется повторять объявления в каждом файле, который их использует. Больше повторений означает больше возможностей для ошибок, а также отсутствие перекрестной проверки между исходными файлами.
Когда объявления записаны в одном заголовке, вы можете быть уверены, что в каждом исходном файле используются одни и те же объявления, и у вас есть только один набор объявлений, который необходимо проверить на наличие ошибок. Кроме того, когда заголовок включен в исходный файл(ы), которые определяют идентификаторы, объявленные в заголовке, компилятор сверит объявления из заголовков с определениями в исходном файле(ах). Это преимущества, которые вы не получите, заполнив отдельно написанные декларации.
При реализации модуля на чистом C, который имеет более одного интерфейса и разделен на «подмодули», как лучше всего обрабатывать внутренние («частные») интерфейсы по сравнению с интерфейсами API?
Это более широкий вопрос, о котором я не буду говорить в целом. Однако я отмечу, что компоновщики или другие инструменты сборки обычно имеют функции для управления видимостью символов. Обычно вы можете структурировать проект:
спасибо за ответ. Действительно, я согласен с вашим анализом, и мы выбрали именно такой подход.
Я бы разделил код на два проекта. Проект 1 будет API и создаст библиотеку (статическую или dll), которая предоставляет только интерфейс общедоступным функциям. Проект 2 будет собственно приложением, которое получает доступ к функциям библиотеки через заголовочный файл, предоставляющий общедоступные функции.