Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проект из многих файлов на C
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
ViKo
Давно терзаюсь вопросами.
Имею проект из нескольких c-файлов. Один из них - главный, main.c, в остальных - некие функции (как разбить проект на части - тоже вопрос смутный, как и понятия "драйвер", "core").
Чтобы функции могли вызываться из любого файла, нужно описать их прототипы. Я их описываю в заголовочных h-файлах, соответствующих c-файлам (display.c - display.h). А чтобы не включать всю кучу h-файлов во все c-файлы, собрал их в одну кучу в main.h. Вот его и включаю во все c-файлы (и в main.c тоже). Где-то видел, такие h-файлы называются "helper".
Чтобы h-файлы не включались в проект по нескольку раз, заключаю все содержимое в макроопределения:
Код
#ifndef DISPLAY_H
#define DISPLAY_H
...
#endif

Также есть отдельные h-файлы, для частных включений.
С глобальными переменными дело обстоит чуть сложнее. В своем файле они должны быть определены, а в остальных заданы, как extern. Для этого пользуюсь макроопределениями (создал в main.h):
Код
#ifndef VAR_DECLS
#define _DECL extern
#define _INIT(x)
#else
#define _DECL
#define _INIT(x) = x
#endif

Только в одном main.c файле задаю определение #define VAR_DECLS, поэтому все переменные будут определены в main.c, независимо от того, в каком h-файле они описаны.

Не слишком ли я запутываю свои проекты? Например, гложет мысль, как правильнее - использовать display.h в main.h или в display.c?
Поделитесь своими решениями. По каким критериям разбиваете проект на файлы? Где храните переменные?
Как передаете глобальные макроопределения (например, #define BUFF_SIZE 4096)?
aaarrr
Цитата(ViKo @ Mar 23 2014, 10:57) *
Не слишком ли я запутываю свои проекты?

По-моему, слишком. Особенно с переменными. Что мешает сделать в main.c:
Код
int global_var = 12345;

А в main.h:
Код
extern int global_var;


Цитата(ViKo @ Mar 23 2014, 10:57) *
...все переменные будут определены в main.c, независимо от того, в каком h-файле они описаны.

Это прямое запутывание: описали в одном месте, а определили в другом, с первым никак очевидно не связанным.
seneka
Цитата(ViKo @ Mar 23 2014, 10:57) *
Давно терзаюсь вопросами.
Как передаете глобальные макроопределения (например, #define BUFF_SIZE 4096)?


Делаю тоже самое, есть куче локальных файлов *.h, эти локальные файлы прописаны внутри main.h, во всех C файлах прописан только main.h
Как там распределяются назначения особо не вникал, но защиту в виде #ifdef поставить в каждом *.h файле надо.
Ну и все, нужно прописать буфер, объявляйте его где хотите среди заголовочных файлов.
ViKo
Цитата(aaarrr @ Mar 23 2014, 11:12) *
Это прямое запутывание: описали в одном месте, а определили в другом, с первым никак очевидно не связанным.

Такого не может быть. Определяется-описывается в одном месте (где угодно, в любом файле, но, очевидно, лучше там, где логичнее) один раз, а попадет во все файлы. Потому что main.h включается во все файлы. Реально определится в main.c, согласно макроопределению _INIT().
aaarrr
Цитата(ViKo @ Mar 23 2014, 12:22) *
Реально определится в main.c, согласно макроопределению _INIT().

Не нужно плодить сущности. Допустим, определению disp_bpp = 16 явно не место в main.c, если есть display.c к которому оно напрямую, а не опосредованно, относится.
ViKo
Цитата(aaarrr @ Mar 23 2014, 11:52) *
Не нужно плодить сущности. Допустим, определению disp_bpp = 16 явно не место в main.c, если есть display.c к которому оно напрямую, а не опосредованно, относится.

Правильно. Так ведь я и задам определение в display.h, откуда оно попадет в main.h, откуда попадет в main.c как определение, и в display.c как объявление.
Так избавляюсь от необходимости определять переменную в display.c и объявлять в остальных файлах (или, хотя бы в display.h).
Думаю сделать так же, как вы говорите, в display.c определять переменную, а в display.h объявлять, как extern. Подобно описаниям функций.
ViKo
Тут вот какой вопрос - если бы переменная или макроопределение использовались только в одном файле, то и глобальными их делать незачем. А если не в одном, то в каком...? rolleyes.gif Где, как не в main.h?
dxp
QUOTE (ViKo @ Mar 23 2014, 13:57) *
Давно терзаюсь вопросами.
<...
А чтобы не включать всю кучу h-файлов во все c-файлы, собрал их в одну кучу в main.h. Вот его и включаю во все c-файлы (и в main.c тоже).

Аналогично, только файл называю не main.h а по имени проекта (<project_name>.h).

QUOTE (ViKo @ Mar 23 2014, 13:57) *
Поделитесь своими решениями. По каким критериям разбиваете проект на файлы? Где храните переменные?

С++ избавляет от подобных дилемм, т.к. позволяет практически не иметь глобальных переменных (в крайнем случае используются статические члены-данные), т.е. переменные в глобальном пространстве имён фактически отсутствуют.
ViKo
Цитата(dxp @ Mar 23 2014, 13:33) *
Аналогично, только файл называю не main.h а по имени проекта (<project_name>.h).

А с-файл как называете - main.cpp или по имени проекта? Тоже колеблюсь туда-сюда. Main найти проще, но смысла меньше.
Помню, за имя Top_m в SV модератор по ПЛИСам намекнул, что отгрыз бы своему "падавану" руки. w00t.gif
AlexandrY
Цитата(ViKo @ Mar 23 2014, 08:57) *
Не слишком ли я запутываю свои проекты? Например, гложет мысль, как правильнее - использовать display.h в main.h или в display.c?
Поделитесь своими решениями. По каким критериям разбиваете проект на файлы? Где храните переменные?
Как передаете глобальные макроопределения (например, #define BUFF_SIZE 4096)?


Нет не запутываете, вы просто копируете в принципе неплохие практики продвигаемые Micrium-ом и другими.

Но это еще приемлемо для небольших проектов в пару десятков файлов.
А если в проекте счет файлам идет на сотни (а это средненький дивайс с GUI, FS, TCP, RTOS ), то этот подход не работает.

Представьте себе main.h с этаким списком в несколько сот заголовочных файлов. Только взглянув на такой поплохеет.
Управлять таким и разруливать взаимозависимости просто катастрофа.
Проект обязательно надо преобразовать в иерархическую структуру.

Поэтому если хотите делать серьезные проекты сразу думайте над иерархией.
Иерархия это например: плата (BSP), платформа (PSP), архитектура/драйвера, приложение.
Также стоит сразу думать о реюзинге. Отсюда может следовать, что не стоит располагать все глобальные переменные в одном файле.
Потом стоит подумать о многозадачности, это может потребовать как можно больше глобальных переменных скомпоновать в структуры чтобы превратить их потом в динамически создаваемые.
И т.д.
Что касается разбивки на файлы, то могу назвать пару критериев.
Как только размер файла подошел к 50 Кб, то его надо как-то разбить, а то время скролинга исходников начинает неявно доминировать в процессе разработки.
Если планируется реюзинг каких-то модулей, то эти модули разделяются на файлы зависимые и независимые от платформы.
Опять же реюзинг заставляет изобретать уровни абстракции, это тоже порождает файлы.
ViKo
Цитата(AlexandrY @ Mar 23 2014, 17:43) *
Представьте себе main.h с этаким списком в несколько сот заголовочных файлов. Только взглянув на такой поплохеет.
Управлять таким и разруливать взаимозависимости просто катастрофа.

Смотрю на примеры с USB, и не въезжаю, что какие файлы делают. Вот их там пара десятков и есть, но не запомнить, где что искать. Много мелких файлов запутывают.
С другой стороны, файлы своих исходников есть и больше 150 KB. Да, искать напрягает, Notepad++ пыхтит вместе со мной, зато знаю, что оно где-то там. rolleyes.gif
Количество файлов как раз под два десятка подбирается. И в них есть и GUI, и RTOS, и DSP. А еще надо и USB, FS...
Пока держу все в одном каталоге. Но Keil уже сам создает несколько папок с исходниками в проекте.
И еще хочется как-то уровнять файлы по размеру, тоже ищу логику, как делить.
AlexandrY
Цитата(ViKo @ Mar 23 2014, 17:10) *
Да, искать напрягает, Notepad++ пыхтит вместе со мной, зато знаю, что оно где-то там. rolleyes.gif


Ну так с этого и надо было начинать. Кто же в Notepad-е делает серьезные проекты.
Брать надо SlickEdit или на худой конец Eclipse со специальными add-on-ами.

А так конечно упретесь. Ни нормального броузинга исходников, ни рефакторинга. Тут хоть где объявляйте хидеры. Легче не станет.
Да еще все в одной директории.
Память не резиновая, не надо ее напрасно тренировать поиском по линейным спискам.
Используйте более гибкие иерархические структуры и деревья. Память их лучше обрабатывает.

ViKo
Цитата(AlexandrY @ Mar 23 2014, 18:56) *
Ну так с этого и надо было начинать. Кто же в Notepad-е делает серьезные проекты.
Брать надо SlickEdit или на худой конец Eclipse со специальными add-on-ами.

SlickEdit не пользовался, в Notepad++ найдется все, что нужно, я думаю. И плагины у него тоже есть.
Вопрос не в редакторе, а в голове.
AlexandrY
Цитата(ViKo @ Mar 23 2014, 18:18) *
SlickEdit не пользовался, в Notepad++ найдется все, что нужно, я думаю. И плагины у него тоже есть.
Вопрос не в редакторе, а в голове.


Если бы это было так, то люди до сих пор кодировали в машинных кодах.
Вопрос в производительности, которая судя по вашему посту начиная с некого объема кода резко упала.

Меня всегда удивляло, что новые языки изобретают не психологи, а гики от программирования.
Потом я понял, что все эти новые синтаксисы это только способ размежевания и борьбы программистов между собой.
А настоящий прогресс несут редакторы и библиотеки.
AnatolyT
Так как до сих пор не делал крупные проекты, типичный проект состоит максимум из 10-15 исходных модулей, стараюсь использовать стандартный подход, к каждому исходнику свой хедер с описанием функций. Все определения типов и свои дефайны определяю в отдельном хедере. Глобальные переменные определяю по мере создания в каждом исходном модуле, потом их легче будет найти по смыслу, использую венгерскую нотацию или скорее похожую на нее. Не определяю крупные массивы для обработки данных, аллокация в хипе в начале каждой обработки, передача через указатель по всей глубине обработки, после обработки освобождаю массив. Стараюсь передавать результат через указатель в формальных параметрах, возврат каждой функции обработки это тип ошибки по всей глубине обработки или 0 если корректное выполнение. Стараюсь не перегружать main.c, только определение прерываний, установка начальных режимов аппаратных модулей и глобальный вызов.
halfdoom
Цитата(ViKo @ Mar 23 2014, 09:57) *
Не слишком ли я запутываю свои проекты? Например, гложет мысль, как правильнее - использовать display.h в main.h или в display.c?


Собирательство всех заголовков в один, не самая хорошая идея. В сообществе разработчиков ОС FreeBSD, есть такой уважаемый человек, как Bruce Evans. Он весьма ревниво следит за стилевой организацией системных .h файлов, да и другого кода. Некоторые из его требований могут показаться надуманными, но если почитать разъяснения, то все становится на свои места. Рекомендую прочесть документ http://www.freebsd.org/cgi/man.cgi?query=style&sektion=9 - в нем много полезных советов.
kolobok0
Цитата(ViKo @ Mar 23 2014, 10:57) *
...чтобы не включать всю кучу h-файлов во все c-файлы, собрал их в одну кучу в main.h....


у компиляторов порой встречается глюк - не чуствительность к изменениям ашника в ашнике.
ну это так...к слову..

а если по теме:
в общий ашник скидывают то, что точно не будет правиться. Ну например файлы объявлений стандартной библиотеки.
Или скажем внешней библиотеки третьих лиц.
Например если взять любой пример от ST то заголовочные файлы включаются в один общий.
Только не забывайте что код в ввиде библиотеки педоставленsm.gif

по поводу консерватории - где и как объявлять глобальные переменные. Так это же вопрос консерватории а не инструмента. Пишите Вы хоть
на си или си плас плас или джаве - фиолетов, это вторично(язык описания). первично логика - кто и как должен видеть, иметь доступ,
править, читать, атомарность доступа, шаринг, время жизни и т.д... аспектов много...
Например:
взять те же самые библиотеки. Удобно писать экспорт-импорт внутри одного ашника. И в зависимости от условия компиляции
объявлять везде что это внешняя переменная либо своя(если внутри либы). При этом объявление носит уникальность записи и исключает двойную
или более трактовку.
dxp
QUOTE (ViKo @ Mar 23 2014, 17:49) *
А с-файл как называете - main.cpp или по имени проекта? Тоже колеблюсь туда-сюда. Main найти проще, но смысла меньше.
Помню, за имя Top_m в SV модератор по ПЛИСам намекнул, что отгрыз бы своему "падавану" руки. w00t.gif

Не, main.cpp присутствует, тут никаких дилемм нет - в нём живёт как правило только функция main(), которая запускает прикладной код. Маленький файл. А заголовок - он содержит проектно-зависимое содержание, поэтому по имени проекта и зовётся.
AnatolyT
Хотел бы добавить к своему сообщению здесь, ничего необычного, но может кому-нибудь будет интересно. Деление проекта на модули разное, но всегда чисто смысловое, по обслуживанию аппаратных модулей, по применяемым алгоритмам обработки, по элементам пользовательского интерфейса и т.п. Размеры модулей разные и думаю что это большого значения не имеет, от 200 строк кода или меньше до 1000-1500 строк, включая необходимые комментарии. В среднем 300-400 строк в модуле. При этом легко можно использовать какой-либо модуль без существенной переделки в других проектах, некоторая унификация)). При желании можно оформить в виде библиотеки. Архив своих проектов также полезен, чтобы не изобретать велосипед заново, было такое что решение для аналогичной задачи нашел на пятидюймовой дискете в своих проектах более чем 10-летней давности.
_Pasha
Цитата(ViKo @ Mar 23 2014, 19:18) *
SlickEdit не пользовался, в Notepad++ найдется все, что нужно, я думаю. И плагины у него тоже есть.
Вопрос не в редакторе, а в голове.

Попробуйте Geany. Лучше нотепадов однозначно.
Вкратце.
Проект в geany - это просто набор открытых файлов. Для маленького проекта никаких дополнительных телодвижений не нужно - TAGS и автодополнения работают автоматом.
Если кол-во файлов такое, что заметны тормоза - делается tags-файл на используемые либы и продолжаете в том же духе.
Но эклипс все равно круче.

ViKo
Цитата(_Pasha @ Mar 24 2014, 08:21) *
Попробуйте Geany. Лучше нотепадов однозначно.
...
Но эклипс все равно круче.

Это всё инструменты.
В Notepad++ я открываю в двух областях разные файлы и копирую, то, что надо, из одного файла в другой. Вот и вся reusability.
Или один и тот же файл редактирую в разных местах. Очень удобно.
halfdoom дал ссылку, там больше про стиль оформления. Многое из того я использую, кое с чем не согласен.

Повторюсь, впадаю в панику, когда вижу 20 файлов, посвященных USB. Это нужно взять лист ватмана, и попытаться на нем нарисовать все связи между элементами. В меньшей степени это относится и ко всем другим библиотечным функциям.
P.S. А как сделать проще для USB - не знаю!
halfdoom
Цитата(ViKo @ Mar 24 2014, 08:47) *
halfdoom дал ссылку, там больше про стиль оформления. Многое из того я использую, кое с чем не согласен.


Полезно, так-же, заглянуть в код этой ОС:

CODE

#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include "usbdevs.h"

#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_process.h>

#include <dev/usb/serial/usb_serial.h>
#include <dev/usb/serial/uftdi_reg.h>


Цитата(ViKo @ Mar 24 2014, 08:47) *
Повторюсь, впадаю в панику, когда вижу 20 файлов, посвященных USB.


Для сложного девайса ничего страшного. Если эти файлы разбиты по функционалу, то ориентироваться в них несложно. Иногда бывает другая крайность - когда задачу дробят на слишком мелкие кусочки, в ущерб общей логике. Тогда конечно, крайне сложно запомнить где что лежит.
ViKo
Цитата(ViKo @ Mar 24 2014, 08:47) *
Это всё инструменты.
В Notepad++ я открываю в двух областях разные файлы и копирую, то, что надо, из одного файла в другой. Вот и вся reusability.
Или один и тот же файл редактирую в разных местах. Очень удобно.

Только что "открыл", что Notepad++ может выдать список функций в файле! И даже отсортировать их по алфавиту, если хочется. Красота.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.