Перейти к содержанию
    

Архитекутра .с.h как добавить в глобальный модуль, другой модуль.

Добрый день подскажите  как соединить две библиотеки .С .H
1) Первая библиотека содержит функции для работы с кольцевым буфером, buf.H файл соответственно содержит структуры(TstackLow) для работы с ними.
2) Вторая библиотека Config.h, это запись системных настроек в ПЗУ. Она глобальная так как настройки могут меняться хоть где(в любом файле) по этому её header добавляю во все хедеры остальных библиотек (добавлю в app.h -> его содержат абсолютно все библиотеки )

Так вот мне понадобилось в  Config.h добавить работу с буфером. Т.е  некий тип который описан в buf.H  после чего компилятор начал ругаться что мне может скомпилировать файл buf.C
и указывает ошибку на Config.h на тип структуры говорит что неизвестна. После чего я начал разбираться почему он её выдает. Оказалось что при компиляции файла buf.с   первоначально инклудиться header  app.h (где находится  Config.h  а в самом файле) он вставляется в верхнюю часть файла  buf.h  а само описание типа находится в buf.h но ниже.  По этому он и выдает ошибку.

Примерно так выглядит config.h и buf.h

#ifndef Config_H_
#define Config_H_
	
#include "Aplication.h"       << тут содержиться Buf.h и др.
........................
TstackLow Test;             <<<<<!!!!!Ошибка!!!!!!!!
#endif
//================================
//================================
//================================
#ifndef Buf_H_
#define Buf_H_
#include "Aplication.h"    
........................
typedef struct {
    unsigned short int Count;
    unsigned short int Tail;
    unsigned short int Head;
    unsigned char Buf[SIZE_STACK_LOW][SIZE_NAME];
}TstackLow;
#endif
	

на выходе buf.c это все разворачивается в

TstackLow Test;             
........................
typedef struct {
    unsigned short int Count;
    unsigned short int Tail;
    unsigned short int Head;
    unsigned char Buf[SIZE_STACK_LOW][SIZE_NAME];
}TstackLow;


Естественно он не может понять. Как это сделать правильнее? Можно конечно перенести  typedef struct {...}TstackLow;  в Config.h  и тогда проблема исчезнет, но не хочу плодить разные сущности в одном файле, если у них есть свой(buf.h)

Возможно это из моей неправильно концепции,  где я создаю app.h  и его инклужу ко все библиотекам, а внутрь его уже добавляю то что может использоваться во всех модулях (тип процессора(stm32f1,stm32f4,..), таймеры, светодиоды, и тд..)

 

 
 

Изменено пользователем pokk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну и каша. Что в коде, что в этой теме. В общем-то как написано, так и работает.

Объявление должно быть раньше, чем использование. Зачем вы делаете определение внутри файла исходников (*.c), если оно используется за пределами этого файла? Такие объявления должны быть в заголовочниках (.h). В файле, где что-то используется, оно тоже должно быть сначала объявлено, да, при этом файл может быть включён в код много раз (#include "somefile.h") и могут произойти переопределения, именно для борьбы с этим в заголовочниках и ставят инклюдгарды (#ifndef FILENAME_H ...)

Изменено пользователем one_eight_seven

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 minutes ago, pokk said:

Добрый день подскажите  как соединить две библиотеки .С .H

И всё-таки это не библиотеки. Это обычные файлы. Файл с расширением *.c - содержит код на Си (для Си++ обычно используют *.cpp), а файл с расширением *.h - это заголовочный файл (хидер в жаргоне, от англ. header). В этом файле обычно размещают объявления: типы, прототипы функций, если это язык Си++, то объявления классов. Иногда там встречается и исполняемый код. Обычно имеется пара-связка foo.c(pp)/foo.h(pp). hpp - обычно хидеры для файлов *.cpp. Но с расширениями тут тоже куча условностей, т.к. компилятору глубоко по-барабану расширение.

Ркомендую в своих хидерах в начале писать не

#ifndef FILE_HPP
#define FILE_HPP

// Тело хидера
#endif

, а более просто

#pragma once

// тело хидера

Меньше вероятность допустить ошибку.

На остальные вопросы вам уже ответили)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

37 minutes ago, one_eight_seven said:

Зачем вы делаете определение внутри файла исходников (*.c)

Ну как в файле buff.c     #include"buf.h" для define  и для всяких регистров (делаю так в каждом модуле,  его header сразу инлужу, а если внутри функции одна зависит от другой то прописываю в header прототипы, но да походу это не правильно)

43 minutes ago, one_eight_seven said:

Объявление должно быть раньше, чем использование

Ну это понятно, в том то и дело что оно само так получается.

buf.c -> buf.h ->app.h-> config.h  ( тут создаю переменную, а определение её идет в buff.h   )

45 minutes ago, one_eight_seven said:

именно для борьбы с этим в заголовочниках и ставят инклюдгарды (#ifndef FILENAME_H ...) 

Это у меня есть.

 

32 minutes ago, haker_fox said:

Ркомендую в своих хидерах 

О благодарю, это все компиляторы воспринимают ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, pokk said:

О благодарю, это все компиляторы воспринимают ?

да, некоторые ещё и оптимизировать это умеют. Этот способ не однозначно лучше, чем метод сиклюдгардами.

2 minutes ago, pokk said:

buf.c -> buf.h ->app.h-> config.h  ( тут создаю переменную, а определение её идет в buff.h   )

Что это за последовательность? Что она призвана прояснить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, one_eight_seven said:

то это за последовательность?

Это последовательность, инклудов файлов, что куда вставляется.

9 minutes ago, one_eight_seven said:

Что она призвана прояснить? 

Как получается что объявление переменной, выше чем использование типа переменной. 

 

PS: Ой на самом  деле я в header не примеренную объявляю, а структуру с с этим типом. (так получилось при выяснении причины ошибки)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не инклудьте лишнего: зачем вам в файле Buf.h нужно #include "Aplication.h"? Что там необходимого именно для файла Buf.h? Такого не должно быть. При правильном подходе, файл работы с буфером не должен иметь зависимостей от глобальных имён и объявлений проекта.

3 hours ago, pokk said:

TstackLow Test;

Это следует убрать из файла Config.h. Выделение памяти под объекты должно происходить не в заголовках, а в .с или .срр файлах, в которых эти объекты будут использованы. Перенесите эту строку в файл, который работает со структурой Test (наверно, это Config.c).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 minutes ago, Darth Vader said:

Это следует убрать из файла Config.h.

Там его и нету, там находиться

typedef union{
    struct{
        uint8_t       ModeNameFile[SIZE_NAME];
        TstackLow     SelectFile1;
    };
    uint8_t     ConfigRead[512];
}ConfigFile_t;

 

28 minutes ago, Darth Vader said:

Что там необходимого именно для файла Buf.h?

Максимальный размер буфера =)) походу пора сделать хедер с конфигурацией где все настройки(define) будут.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, one_eight_seven said:

да, некоторые ещё и оптимизировать это умеют. Этот способ не однозначно лучше, чем метод сиклюдгардами.

Есть какие-то тонкие моменты?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 minutes ago, haker_fox said:

Есть какие-то тонкие моменты? 

Тонких нет.

Но компилятор смотрит по полному пути файла. Итак, если у вас один и тот же файл каким-то образом лежит в нескольких местах, то макро будет определено при первом включении файла, и переназначений не произойдёт, прагма же попытается включить код второй раз (файл-то другой).

Более того, symlink'и и hardlink'и ещё сильнее усложняют задачу определения того, что файл на самом деле один и тот же.

Ну и используя макроопределения можно строить условные компиляции.

Как абстрактрый пример, у вас есть несколько разных версий драйвера, которые требуют разного кода для своего использования. С помощью директив условной компиляции вы можете их разделить, в случае pragma, тоже можете. но только тем же самым способом, и у вас к прагме прибавится инклюдгард, и плюсы прагмы уже перестанут быть актуальными.

 

Потому, стандартный путь, классический - это инклюдгарды.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 

В файл Config.h  необходимо добавить строку #include Buf.h

Тогда тип  TstackLow в файле Config.h будет объявлен до того, как будет использован для построения типа ConfigFile_t.

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

Это должно решить вашу проблему.

Изменено пользователем Darth Vader

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 hours ago, Darth Vader said:

Ну и файл app.h по-хорошему должен содержать лишь макроопределения,

C модулем buf, это да согласен, он не должен зависеть, от того какой там процессор используется.

Тогда как подключать тип процессора и отладочный светодиоды ? в каждый модуль .h вставлять ? И если серия изменилась, то во всех исходника править её?

А если взять другой пример, который зависит от периферии, к примеру таймер, в header  timer.h описал свой тип для работы с ним, добавил туда  app.h (чисто с макроопределениями и типом процессора), это все добавил в  Config.h и его уже инклудить только к тем модулям, в которых он нужен.  Просто бывает он нужен во многих, так что он будет стоят почти везде и при переносе одного модуля в другой проект эти спагетти будут тянуться...

 

 

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 hours ago, pokk said:

Тогда как подключать тип процессора и отладочный светодиоды ? в каждый модуль .h вставлять ? И если серия изменилась, то во всех исходника править её?

Скачайте gcc для любого микроконтроллера и посмотрите, как это сделано там. Обычно это файл mcu.h там заголовочные файлы для конкретных моделей MCU обмазаны толстым слоем директив препроцессора. Эти директивы основаны на макроопределениях. Макроопределения эти передаются компилятору как параметр командной строки (даже если вы гуй удовлетворяете, мышкой тыкая, в конечном итоге это вызов консольной команды).

Т.е. вы это макро явно определяете при настройке инструмента сборки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Посмотрите, как реализован выбор конкретного процессора в SPL. Сначала надо определить один из нескольких макросов, соответствующих типу используемого процессора (раскомментировать нужную строку с дефайном). А уже потом в зависимости от того, какой макрос определён, препроцессором в директивах условной компиляции инклудится файл с описанием регистров нужного процессора. Так же задаётся тип отладочной платы, если она есть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...