API и «частные» (внутренние) функции в модуле C

При реализации модуля на чистом C, который имеет более одного интерфейса и разделен на «подмодули», как лучше всего обрабатывать внутренние («частные») интерфейсы по сравнению с интерфейсами API? (Очевидно, что в C нет функции Private/protected как таковой, но есть способы сделать что-то.)

Пример:

my_module имеет два интерфейса: один для кода нижнего уровня (например, HAL), а другой для приложения. Итак, в папке включения у меня есть:

my_module_HAL.h
my_module_API.h

и в исходной папке есть соответствующие файлы .c

Однако внутри функции, реализованные в my_module_XXX.c, должны вызывать друг друга. Как лучше всего это сделать? Я вижу несколько способов, какие плюсы и минусы у каждого?

  1. иметь больше файлов .h в исходной папке, называемых примерно так _my_module_HAL.h или каким-либо другим соглашением об именах
  2. используйте ключевое слово extern в исходных файлах

Есть ли веская причина предпочесть одно другому? Есть ли другие альтернативы, которые лучше? Обратите внимание, что мы также хотим сделать модульное тестирование кода максимально простым и чистым, используя 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, и эта структура, похоже, вполне хорошо с этим работает. У нас хорошая модульность кода и по названиям файлов видно, что они делают и куда к ним следует обращаться. Очень доволен этим решением.

Я бы разделил код на два проекта. Проект 1 будет API и создаст библиотеку (статическую или dll), которая предоставляет только интерфейс общедоступным функциям. Проект 2 будет собственно приложением, которое получает доступ к функциям библиотеки через заголовочный файл, предоставляющий общедоступные функции.

OldBoy 08.08.2024 10:39

Я бы сохранил частные файлы заголовков вместе с файлами .c для модуля и поместил общедоступные заголовки в каталог включения. Также старайтесь избегать загрязнения пространства имен во внешне связанных частных функциях.

Ian Abbott 08.08.2024 11:02

@danmcb. Здесь 5 вопросов (некоторые весьма субъективны). Рассмотрите возможность сокращения набора вопросов.

chux - Reinstate Monica 08.08.2024 12:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как лучше всего это сделать? Я вижу несколько способов, какие плюсы и минусы у каждого?

иметь больше файлов .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?

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

  • Различные модули библиотеки компилируются в объектные модули.
  • Объектные модули связаны между собой в статическую или динамическую библиотеку.
  • Во время ссылки или использования другого инструмента после ссылки символы, которые должны быть внутренними по отношению к проекту, могут быть удалены или отключены в библиотеке, чтобы клиенты библиотеки не могли ссылаться на них. Символы, которые должны быть внешними, останутся в покое и будут доступны для использования клиентами.

спасибо за ответ. Действительно, я согласен с вашим анализом, и мы выбрали именно такой подход.

danmcb 12.08.2024 09:06

Другие вопросы по теме