Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 16:36:09

Всем привет, проект вырос, стало затруднительно продолжать разработку

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

Особенность всех IDE, и условно стандартной обработки через make all - например в блокноте - подразумевает сборку всех .cpp файлов в .o - по условию %.cpp - отсюда проблема видимости разными классами - других классов, и упорядочить include - не представляется возможным, т.е.:

#include one
#include two
#include three

one не будет видеть two и three т.к. они вызываются позже, как не тусуй очередность - всегда будет задействован какой либо функционал еще не подключенного файла

Предварительное объявление не актуально, эту роль выполняет .h

Еще при сборке .cpp от .h - есть проблема видимости констант и инклюдов указанных в main.cpp

Если собирать только один файл main.cpp - все собирается прекрасно, но времени уходит на сборку - куча, .cpp => .o => .elf => .bin, можно конечно какой нибудь пакетный файл сделать, но не хотелось бы отходить от makefile

Кто то встречал решения по данной проблеме, или может кто то разрабатывает что то серьёзнее чем однофайловый проект и тоже сталкивался ?

Может какие то директивы есть для указания того что файл .cpp является дополнением для заголовочного .h и его не надо обрабатывать до обработки файлов без директив?

Убрать использование одного класса из другого и передавать ссылки\указатели - не вариант, это будет over 9000 указателей для обработки нужных данных

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:12:10

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

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:18:52

"Мешанинина" у Вас какая-то.

Особенность всех IDE, и условно стандартной обработки через make all - например в блокноте - подразумевает сборку всех .cpp файлов в .o - по условию %.cpp - отсюда проблема видимости разными классами - других классов, и упорядочить include - не представляется возможным, т.е.:


В си/С++ раздельная компиляция. Т.е. каждый файл *.c/*.cpp компилируется независимо от других. Из этого Вы и должны исходить.
Если не рассматривать, совсем редкие и экзотические случаи, то *.c/*.cpp вообще никогда никуда не инклудятся.

Может какие то директивы есть для указания того что файл .cpp является дополнением для заголовочного .h и его не надо обрабатывать до обработки файлов без директив?


Как *.cpp может являться дополнением *.h файла??? *.cpp - это исходники. В *.h файлах вообще не должно быть того, что генерирует код.
Я бы сказал, что *.h файл является дополнением c/cpp файла (если предположить, что слово "дополнение" здесь может быть применимо), так как в нем описан интерфейс использования объектов/функций из реализация которых находится в *.с/с++ файлах.

===
ЗЫ. привели бы маленький пример, в котором видны Ваши проблемы. Лично я не понял, что вызывает у Вас затруднения.
Последний раз редактировалось viiv Ср июл 18, 2018 17:30:58, всего редактировалось 2 раз(а).

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:21:39

Вынес все классы в разные .h файлы с описанием в .cpp

Это как? Тоже хотелось бы глянуть на пример...

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:37:59

Что-то мне подсказывает, что ТС если про препроцессор если и слышал, то не видел :)

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:43:45

V2oD2o, вы сперва с просто С разберитесь и основами компиляции, а уж потом на плюсы посматривайте.
И если это вАААще первое ваше знакомство с яву и МК , то плюсы вообще крайне противопоказаны ...

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 17:44:37

Всем привет, проект вырос, стало затруднительно продолжать разработку

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

А зачем классы внутри класса если это только не наследование? Скорее всего в этом и проблема. Класс - это самодостаточный объект, и он не должен зависеть от наличия другого класса и редко, когда внутри класса используются внешние объявления переменных или констант.
Все что нужно, передается в класс либо частями, либо в виде структуры, так проще.

Особенность всех IDE, и условно стандартной обработки через make all - например в блокноте - подразумевает сборку всех .cpp файлов в .o - по условию %.cpp - отсюда проблема видимости разными классами - других классов, и упорядочить include - не представляется возможным, т.е.:

#include one
#include two
#include three

one не будет видеть two и three т.к. они вызываются позже, как не тусуй очередность - всегда будет задействован какой либо функционал еще не подключенного файла

Если все привести в порядок, то будет все видеться. Ведь инклуды от классов, это всего лишь предварительные объявления.
Если класс самодостаточный, то он будет использоваться в головном файле, вы же все равно создадите объект.
Код:
#include one
#include two
#include three
...
one *Class1;
two *Class2;
three *Class3 ;
.....
Class1->init();
Class1->Dofunc1(&struct1);
Class1->exit();
delete Class1;
....
....

И юзайте их вдоль и поперек и инклуды вешайте как угодно.
Если надо класс сделать в классе, то подумайте, может проще сделать наследование и добавить нужные методы,
чем делать перекрестные ссылки.

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 19:02:18

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


Вопрос не понят, да и библиотеки не используются

Добавлено after 1 minute 49 seconds:
V2oD2o, вы сперва с просто С разберитесь и основами компиляции, а уж потом на плюсы посматривайте.
И если это вАААще первое ваше знакомство с яву и МК , то плюсы вообще крайне противопоказаны ...


не по теме

Добавлено after 11 minutes 9 seconds:
Что то видимо без примера сложно понять..

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 19:05:24

Вопрос не понят
Давайте с чего-нибудь начнём. Вот пример небольшого класса. Что из этого непонятно?

hmc704t.h
Спойлер
Код:
#ifndef HMC704T_H
#define HMC704T_H

namespace HMC704T_NAMESPACE
{
  using namespace IOPORTS;

  typedef PD_2  LE1;
  typedef PC_15 LE2;
  typedef PA_8  LE3;
 
typedef struct
{
  uint8_t HiKcp;
  uint8_t CP;
} HMC704_CP;

template <const uint8_t NUM, class LE_PIN>
class HMC704T
{
  public:
    HMC704T()  { chan = 0; };
    void SetHMCMode(void) { LE=1; __no_operation(); LE=0; };
    void SetChannel(uint8_t);
    auto GetChannel(void) { return chan; };
    void FirstLoad(uint8_t);
  private:
    void SetFreq(uint16_t f);
    void SetReg(uint8_t reg, uint32_t value);
    uint8_t chan;
    LE_PIN LE;
    PC_10 SCK;
    PC_12 DATA;
    PC_13 SW3;
};

} //namespace HMC704T_NAMESPACE

namespace SNT=HMC704T_NAMESPACE;

#ifdef HMC704T_CPP 

  SNT::HMC704T<1,SNT::LE3> snt1;   // Синтезатор ПРМ 10.0-13.0 ГГц  (по схеме на LE3)
  SNT::HMC704T<2,SNT::LE2> snt2;   // Синтезатор ПРД 10.0 ГГц       (по схеме на LE2)
  SNT::HMC704T<3,SNT::LE1> snt3;   // Синтезатор ПРД 10.0-11.5 ГГц  (по схеме на LE1)
 
  template class SNT::HMC704T<1,SNT::LE3>;
  template class SNT::HMC704T<2,SNT::LE2>;
  template class SNT::HMC704T<3,SNT::LE1>;
 
#else
 
  extern SNT::HMC704T<1,SNT::LE3> snt1;
  extern SNT::HMC704T<2,SNT::LE2> snt2;
  extern SNT::HMC704T<3,SNT::LE1> snt3;
   
#endif //HMC704T_CPP
 
#endif //HMC704T_H

hmc704t.cpp
Спойлер
Код:
#define HMC704T_CPP

#include "main.h"

namespace HMC704T_NAMESPACE
{

// Задать номер канала 0-60
template <const uint8_t NUM, class LE_PIN>
void HMC704T<NUM,LE_PIN>::SetChannel(uint8_t channel)

  if(NUM==1) //У синтезатора ПРМ переключаемый ГУН
  {
    if(channel>28) SW3=1; else  SW3=0;
  }
  if(channel>60) channel=60;
  SetFreq(channel*50+10000);
  chan=channel;
}

// Первая загрузка с частотой из EEPROM
template <const uint8_t NUM, class LE_PIN>
void HMC704T<NUM,LE_PIN>::FirstLoad(uint8_t ch)
{   
  SetReg(0x00,(1<<5));     //Генерировать RESET
  SetReg(0x01,(1<<1));     //Chip Enable
  SetReg(0x02,2);          //R=2
  //SetReg(0x06,0x030F4A);   //Frac
  SetReg(0x06,0x0307CA);   //Int
  SetReg(0x07,0x00014D);   
  SetReg(0x08,0x09BEFF);    //Analog EN
  SetReg(0x0B,0x078071);
  SetReg(0x0F,0x000001);
  SetChannel(ch);   
  chan=ch;
};

extern const HMC704_CP cpVSfreq[];

// Задать частоту c шагом 1 МГц
template <const uint8_t NUM, class LE_PIN>
void HMC704T<NUM,LE_PIN>::SetFreq(uint16_t f)
{
  uint8_t LEAK_MAG = 80; // 0.4 мА
  uint8_t chan = (f-10000)/50;
  uint8_t HiKcp = cpVSfreq[chan].HiKcp;
  uint8_t CP = cpVSfreq[chan].CP;
 
  uint8_t intg = f/200;
  uint32_t frac = (f%200)*4194304L/50;
 
  if(frac==0)
  {
    SetReg(0x06,0x0307CA);   //Int
  }
  else
  {
    SetReg(0x06,0x030F4E);   //Frac
  }
         
  SetReg(0x03,intg);
  SetReg(0x04,frac); 
  SetReg(0x09, ((uint32_t)HiKcp<<23) | (1L<<22) | ((uint32_t)LEAK_MAG<<14) | (CP<<7)| CP); //CP_DN

};

template <const uint8_t NUM, class LE_PIN>
void HMC704T<NUM,LE_PIN>::SetReg(uint8_t reg, uint32_t value)
{   
  uint32_t tmp = ((uint32_t)(reg&0x3F)<<25)|((value&0x00FFFFFF)<<1);
  LE=1;
  for(auto mask=0x80000000U;mask;)
  {
    SCK=0;   
    DATA = (tmp & mask) ? 1 : 0;
    mask >>=1;
    SCK=1;
  }
  __no_operation(); __no_operation(); __no_operation();
  SCK=0;
  LE=0;
};

const HMC704_CP cpVSfreq[61] = {  //  { HiKcp, CP }
  { 1, 0  }, { 1, 5  }, { 1, 10 }, { 1, 15 },    // 10'000  10'050  10'100  10'150   
  { 1, 20 }, { 1, 25 }, { 1, 30 }, { 1, 35 },    // 10'200  10'250  10'300  10'350
  { 1, 40 }, { 1, 45 }, { 1, 50 }, { 1, 50 },    // 10'400  10'450  10'500  10'550
  { 1, 50 }, { 1, 50 }, { 1, 50 }, { 1, 50 },    // 10'600  10'650  10'700  10'750
  { 1, 50 }, { 1, 50 }, { 1, 50 }, { 1, 50 },    // 10'800  10'850  10'900  10'950
  { 1, 65 }, { 1, 65 }, { 1, 65 }, { 1, 65 },    // 11'000  11'050  11'100  11'150
  { 1, 65 }, { 1, 75 }, { 1, 75 }, { 1, 75 },    // 11'200  11'250  11'300  11'350
  { 1, 75 }, { 1, 0 }, { 1, 0 }, { 1, 0  },      // 11'400  11'450  11'500  11'550
  { 1,  5 }, { 1,  5 }, { 1,  5 }, { 1, 5  },    // 11'600  11'650  11'700  11'750
  { 1,  5 }, { 1, 10 }, { 1, 15 }, { 1, 20 },    // 11'800  11'850  11'900  11'950
  { 1, 25 }, { 1, 25 }, { 1, 30 }, { 1, 30 },    // 12'000  12'050  12'100  12'150
  { 1, 35 }, { 1, 35 }, { 1, 40 }, { 1, 40 },    // 12'200  12'250  12'300  12'350
  { 1, 45 }, { 1, 45 }, { 1, 50 }, { 1, 50 },    // 12'400  12'450  12'500  12'550
  { 1, 55 }, { 1, 55 }, { 1, 60 }, { 1, 65 },    // 12'600  12'650  12'700  12'750
  { 1, 65 }, { 1, 65 }, { 1, 65 }, { 1, 65 },    // 12'800  12'850  12'900  12'950
  { 1, 65 } };                                   // 13'000
// 5=0,1 мА  25=0,5 мА  50=1,0 мА 65=1,3 мА 75=1,5 мА

}

main.h
Спойлер
Код:
#ifndef MAIN_H
#define MAIN_H

#include "stm32f107xc.h"
#include  <intrinsics.h>
#include <stdint.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include "SEGGER_RTT_Conf.h"
#include "SEGGER_RTT.H"

#include "gpio_stm32f1.h"

#include "lcd.h"
#include "Keyboard.h"
#include "Eth_Adresses.h"
#include "Eth.h"
#include "hmc629.h"
#include "hmc704T.h"
#include "EEPROM.h"
#include "Delay.h"
#include "usb_interrupt.h"
#include "usb_device.h"
#include "usb_cdc.h"

#define HSE_VALUE 8'000'000UL
#define CPU_FREQ 72'000'000UL

#endif //MAIN_H

main.cpp
Спойлер
Код:
#include "main.h"

int main()

  //Перевод hmc704 в режим HMC Mode
  snt1.SetHMCMode();
  snt2.SetHMCMode();
  snt3.SetHMCMode();
   
  snt1.FirstLoad(0);
  snt2.FirstLoad(0);
  snt3.FirstLoad(0);
 
  for(;;)
  {     
    if(usb.isCommandReceived()) tna.DoCommand();         
  };
}


V2oD2o, вы сперва с просто С разберитесь и основами компиляции, а уж потом на плюсы посматривайте.
Вместе с тем, в плюсах есть такая штука как namespace, которая сильно помогает в этом деле.

либо в виде структуры
В плюсах класс и структура это одно и то же, за мелким отличием.

Re: Для профи, сборка разбитого проекта по header'ам

Ср июл 18, 2018 22:29:33

Кстати, а для чего вот это - #define HMC704T_CPP ?
И потом вот это #ifdef HMC704T_CPP ....
Зачем делать:
#define HMC704T_CPP
#include "main.h"
Объявления внешних констант можно было и в .h запихать (extern const HMC704_CP cpVSfreq[];),
чего им делать в .cpp

И смысл использовать выбор шаблона класса в препроцессоре ?
Не проще бы просто вначале того же main.h при компиляции 2 строчки раскомментировать и все ?
Или проще вспоминать, где же там идет выбор шаблона...
Да и объявление класса можно было в main тогда вынести и было бы видно где он объявляется,
а не гадать. Проще даже отлаживать.

Структура и класс это почти одно и тоже, разве что некоторые формальности опущены. Но это тут ни причем.

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

Re: Для профи, сборка разбитого проекта по header'ам

Чт июл 19, 2018 05:38:59

Кстати, а для чего вот это - #define HMC704T_CPP ?
И потом вот это #ifdef HMC704T_CPP ....
Зачем делать:
#define HMC704T_CPP
#include "main.h"
Потому что, в заголовочном файле есть часть кода, которая предназначена только для "своего" модуля. Глобальные переменные и экземпляры самого класса, которые должны быть видны наружу, я объявляю в заголовочном файле, так как это часть интерфейса модуля. Надо продублировать объявление и extern на него. Я делаю это в одном месте - так проще писать и тяжелее ошибиться. Если делать это в разных местах, то труднее контролировать всё ли объявлено и одинаково ли объявлено. Конечно же, компилятор, ткнёт носом, но потом по разным файлам не надо лазить, чтобы привести всё в соответствие. Или, например, объявлены выходы процессора, к которым подключен синтезатор. Когда я буду использовать этот класс в другом проекте, то подключу заголовочный файл, поправлю только в нём определение выводов и всё. Всё что касается описания/параметризации интерфейса модуля сосредоточено в одном месте. А так как вне модуля никому не положено знать про ножки к которым подключен синтезатор, то они спрятаны под ифдеф и как бы сосланы в cpp. Тем более, что выводы это не просто какие-то дефайны, а серьёзные классы из другого пространства имён.

Объявления внешних констант можно было и в .h запихать (extern const HMC704_CP cpVSfreq[];),
чего им делать в .cpp
А что ей делать в .h? Она закрытая, используется только в одном методе класса, зачем её светить всему миру? В заголовочном файле только интерфейс модуля. Возможно, вас смутил extern, но в данном случае он не светит наружу массив, а лишь предварительно описывает его тип чтобы можно было использовать в методе класса. А само определение массива в конце модуля, чтобы не засорять реализации методов мусорными данными.

И смысл использовать выбор шаблона класса в препроцессоре ?
Не проще бы просто вначале того же main.h при компиляции 2 строчки раскомментировать и все ?
Или проще вспоминать, где же там идет выбор шаблона...
Шаблоны это вообще отдельная тема. Я специально пример с шаблоном взял, чтобы вопросы появились. С шаблонами всегда идёт борьба с компилятором, чтобы он инстанцировал все нужные реализации да ещё не там где он хочет, а там где хочу я, чтобы сохранить модульность программы. Творческий процесс всегда :) В данном случае я создаю три объекта класса синтезатора и объявляю интерфейс к ним. Другие модули ничего о шаблонах знать не должны, они знают лишь что есть эти три объекта и как ими пользоваться.

Да и объявление класса можно было в main тогда вынести и было бы видно где он объявляется,
а не гадать. Проще даже отлаживать.
Почему в main? Он и в других модулях используется. Нет уж, всё что касается объявлений класса должно быть сосредоточено в модуле в котором он живёт. Зачем мне по всему проекту это размазывать. Чтобы использовать классы/объёкты модуля, достаточно просто подключить его заголовочный файл. Всё что нужно для работы модуля, написано/скрыто внутри модуля.

Структура и класс это почти одно и тоже, разве что некоторые формальности опущены. Но это тут ни причем.
Мимо :) У структуры кишки по умолчанию public, а у класса private - вот и всё отличие.

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

Честно говоря, наверное надо просто составить структуру программы, классы сделать без вставок препроцессоров и будет все проще и понятней. Честно говоря, тяжело будет кому-то потом это все понимать.
Можете на моём примере показать как?

Классы мелкие, думаю можно их переправлять постепенно.
Но как говорится, работает... ничего не меняй.
Что вы там переправлять собрались? Всё написано и работает ровно так как задумано.

Спасибо за содержательные вопросы, думаю ТС будет над чем подумать.

Re: Для профи, сборка разбитого проекта по header'ам

Чт июл 19, 2018 15:06:45

one не будет видеть two и three т.к. они вызываются позже, как не тусуй очередность - всегда будет задействован какой либо функционал еще не подключенного файла

Мне конечно не сложно повторить, но, чего вы ожидали, если ваш класс объявляется сразу после описания его реализации?
Если вы его не объявите, то у вас будут ошибки в компиляции, т.к. другие классы ничего еще о нем не знают.
А если вы его просто не хотите использовать больше, то что еще отвалится?
Я в свое время от этого отказался, т.к. иногда лишние модули становятся не нужны, и я просто в main комментирую 1 строку
и нужные блоки (можно даже через ifndef это сделать, если проектов много, а функционал часто используется).
Чтобы из 10 модулей оставить только 2, я даже не думаю, что там у них внутри. Просто выключаю в головном файле лишнее
и компилю. Если через препроцессор, то закомментировать 8 дефайнов куда проще, чем искать, что там еще отвалится.
Как-то так.

Re: Для профи, сборка разбитого проекта по header'ам

Чт июл 19, 2018 15:13:27

Надо будет реальный пример собрать, а то кто в лес кто по дрова, суть всего выше-написанного понятна, но без примера разговор не предметный :)
Ответить