Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Объявления
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
SasaVitebsk
Раньше у меня не было таких проблем. Обычно создавал группу файлов. Как правило все файлы были достаточно обособленными и я их объединял на этапе линковки. Если были групповые общие объявления, то я их делал в main, а в других объявлял как extern.

Проблема возникла в одном проекте. Он получил развитие и теперь существует в нескольких реинкарнациях с различными модификациями и процами. Тем не менее там есть очень крупные общие блоки. Столкнувшись, с проблемой поддержки - пришёл к необходимости общую часть исходников выделить в отдельные файлы/процедуры. А иначе, при внесении изменений приходилось править все проекты. Выделил и переписал, с учётом унификации.

А теперь вопрос.
Эти блоки используют общие глобальные переменные. Как их правильно объявить, чтобы было удобнее использовать и поддерживать, а также было меньше разной писанины?
rezident
Может стоит объединить переменные в глобальную структуру?
sigmaN
Не знаю правда ли, но я недавно прочел такую вещь:
Цитата
56. Не захламляйте область глобальных имен
Беспорядок в области глобальных имен является характерной проблемой
для среды групповой разработки. Вам не очень понравится спрашивать
разрешение от каждого участника группы каждый раз, когда вы вводите
новый идентификатор. Поэтому:
• Локальная переменная всегда более предпочтительна, чем член класса.
• Член класса всегда более предпочтителен, чем статическая глобальная
переменная.
• Статическая глобальная переменная всегда более предпочтительна,
чем настоящая глобальная переменная.
Статический глобальный идентификатор не экспортируется из файла .c,
поэтому он невидим из других модулей. Применяйте модификатор
static к как можно большему числу глобальных идентификаторов
(переменным и функциям). Ключ доступа private в определении класса
еще лучше. Идентификатор, определенный локально внутри
подпрограммы, лучше всех, потому что он изолирован от всех других
функций в программе.
Вывод: избегайте препроцессора. Так как у макроса такая большая
область видимости, то он, по сути, то же самое, что и глобальная
переменная.
Ален И. Голуб Правила программирования на Си и Си++
А ещё где-то он писал, что за более чем 20летнюю практику программирования он может сказать, что реально обычно требуется пять-десять действительно глобальных переменных на большой проект.
Поэтому Вам следует пересмотреть тактику проектирования модулей и, возможно всё станет куда более прозрачно.

P.S. книгу тоже к прочтению рекомендую, хотя тут прозвучала мысль, что она уже очень старая и некоторые вещи сейчас могут утратить актуальность...это тоже похоже на правду.
В общем её(как и всё в этой жизни) нужно использовать с умом smile.gif
SasaVitebsk
Цитата(rezident @ Oct 14 2009, 22:13) *
Может стоит объединить переменные в глобальную структуру?

Проект достаточно крупный. Переменных много. В том числе и структуры тоже есть.

Цитата(sigmaN @ Oct 14 2009, 22:25) *
А ещё где-то он писал, что за более чем 20летнюю практику программирования он может сказать, что реально обычно требуется пять-десять действительно глобальных переменных на большой проект.

В общем так обычно у меня и было. Поэтому вопрос и не возникал. А тут специфика.
Представте что модуль выполняет обработку. А-В-С. Причём часть тела модуля B является специфической для разных проектов.
Если не резать, то всё нормально, но тогда придётся поддерживать 4 разных проекта. Но часть B является незначительной по сравнению с объёмом самого проекта. Остальные части удалось формализовать. Частично пришлось переписать с прицелом на "универсальность". В результате со всеми проектами можно работать по единым правилам. А то раньше - где-то подправишь - в другом вылазит.

За книгу спасибо, попробую почитать.
sigmaN
Ну тут, ИМХО, кроме Вас никто не решит проблему.
Я иногда по два-три раза переписываю модули. И причиной является именно неудачное сцепление с остальными smile.gif
Хотя на меня ровняться не стоит особо - я ещё относительно неопытный программист.
Faradey
как вариант, объедините специфические "переменные" в группы и передавайте в исп. ф-ции указатель на них:
Код
extern MyType my_type;
extern int f1( void *p );

...

int res = f1( ( void *)&my_type );
kurtis
Цитата(SasaVitebsk @ Oct 14 2009, 22:38) *
В общем так обычно у меня и было. Поэтому вопрос и не возникал. А тут специфика.
Представте что модуль выполняет обработку. А-В-С. Причём часть тела модуля B является специфической для разных проектов.
Если не резать, то всё нормально, но тогда придётся поддерживать 4 разных проекта. Но часть B является незначительной по сравнению с объёмом самого проекта. Остальные части удалось формализовать. Частично пришлось переписать с прицелом на "универсальность". В результате со всеми проектами можно работать по единым правилам. А то раньше - где-то подправишь - в другом вылазит.


Попробуй-те использовать С++ !
Пишете какой-то виртуальный базовый класс-родитель, который имеет только интерфейс, никакого кода, а потом наследуете каждый конкретный модуль от базового класса и пишете свой, специфический для данного модуля код (т.е. будет класс_А, класс_Б и тд). Константы и перечисления можно запрятать в класс и никто снаружи их не будет видеть. Ну а дальше уже пишите технологию, которая через интерфейс базового класса работает с Вашими модулями. Ну и для пущей уверенности, запихать все по своим пространствам имен (namespace).
SasaVitebsk
Цитата(kurtis @ Oct 15 2009, 00:32) *
Попробуй-те использовать С++ !

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

Я просто думал, что существуют какие-то "стандартные решения" на этот случай.

Всем ответившим - спасибо.
smile.gif
zltigo
Цитата(kurtis @ Oct 15 2009, 00:32) *
Константы и перечисления можно запрятать в класс и никто снаружи их не будет видеть....

Для собственно данных существенных преимуществ перед банальным запихиванием всего вышеперечисленного в банальную сишную структуру нет.
Dima_G
Ранее тоже столкнулся с тем, что большая часть кода активно используется в нескольких проектах. Соответственно выделил ее в некоторый фреймворк. Фреймворк тоже состоит из двух частей - платформонезависимой и платформозависимой.

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

Все это написано на С++
Если будет интерес - могу выложить код
dxp
Цитата(kurtis @ Oct 15 2009, 04:32) *
Попробуй-те использовать С++ !
Пишете какой-то виртуальный базовый класс-родитель,

Вы хотели сказать "абстрактный базовый класс"?

Цитата(SasaVitebsk @ Oct 15 2009, 05:24) *
Похоже что было бы оптимально. Там и так уже практически готовая работа с объектами.

Тоже за С++. Только не уверен, что тут нужна иерархия полиморфных классов (т.е. с виртуальными функциями). Мне кажется, что тут достаточно будет просто определить интерфейсы (как там что с чем взаимодействует), и из этого уже автоматом получаются интерфейсы классов - фукнции-члены в public части класса. Все, далее представление и реализации классов можно будет спокойно менять - пока интерфейсы неизменны, совместимость будет на месте. Таким образом можно отделить интерфейсы от реализации. В объектом программирование оно называется инкапсуляцией.

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

Цитата(zltigo @ Oct 15 2009, 05:41) *
Для собственно данных существенных преимуществ перед банальным запихиванием всего вышеперечисленного в банальную сишную структуру нет.

Данные - они не сами по себе, с ними код работает. И данные лучше спрятать подальше от произвольного доступа, а наружу выдать только допустимые методы работы с данными. Поэтому данные надо не в структуру спрятать, а в класс, в private секцию. Разница с сишной структурой будет более, чем значительна.
Flexz
Вобщем-то, все что вы предлагаете можно и на С реализовать, не применяя классы.
Абстрактный базовый класс? прототипы функций в .h-файле
private-секция? static структура.
Нужно несколько экземпляров одинакового модуля в рамках одного проекта? Передавайте указатель на эту структурку, преобразовав его к void*, дабы вызывающий код не лазил куда не следует (идиома pimpl).
Несколько реализаций одного базового (абстрактного) класса, в данном конктретном случае, как я понял, не нужны, но и их при желании можно реализовать с помощью структуры с набором указателей на функции.

ИМХО в данном случае это лучше, т.к. перетащить большой готовый проект с С на С++ в одно действие не выйдет.
Dima_G
Цитата(Flexz @ Oct 15 2009, 10:07) *
ИМХО в данном случае это лучше, т.к. перетащить большой готовый проект с С на С++ в одно действие не выйдет.


Никто не спорит, что можно заниматься объектным программированием на Си, Другое дело - насоклько это удобно (ИМХО - как ездить на велосипеде без педалей. Можно, но пешком быстрее)) ).

Насчет приведения к (void*) - это путь для всевозможных трудноуловимых ошибок, тк рано или поздно будет передан не тот объект, который ожидаем smile.gif
Одно из достоинств С++ - строгий контроль за типом данных. Да простейший пример - очереди. Попробуйте реализовать универсальную очередь на Си и на С++, и почувствуйте разницу smile.gif
dxp
Цитата(Flexz @ Oct 15 2009, 14:07) *
Вобщем-то, все что вы предлагаете можно и на С реализовать, не применяя классы.
Абстрактный базовый класс? прототипы функций в .h-файле
private-секция? static структура.
Нужно несколько экземпляров одинакового модуля в рамках одного проекта? Передавайте указатель на эту структурку, преобразовав его к void*, дабы вызывающий код не лазил куда не следует (идиома pimpl).

Не берусь судить в целом, но советовать использовать void* ни в коем случае не стал бы. Работа через void* - это хак, и в если его где и оправдано применять, так не от хорошей жизни. Случаи использования void* надо сводить к минимуму, в идеале - к нулю.

Цитата(Flexz @ Oct 15 2009, 14:07) *
Несколько реализаций одного базового (абстрактного) класса, в данном конктретном случае, как я понял, не нужны, но и их при желании можно реализовать с помощью структуры с набором указателей на функции.

ИМХО в данном случае это лучше, т.к. перетащить большой готовый проект с С на С++ в одно действие не выйдет.

Может и лучше. Но если уж оставаться на С, то и концепции С++ лучше не тащить, от них при ручной реализации будет больше проблем, чем пользы.
Flexz
Цитата
Насчет приведения к (void*) - это путь для всевозможных трудноуловимых ошибок, тк рано или поздно будет передан не тот объект, который ожидаем

Это элементарно контролируется сигнатурой и/или sizeof.

И вообще меня неправильно поняли - я не предлагаю тащить в С концепции С++, а лишь провожу аналогии - это все там и так есть, просто приглядитесь повнимательнее.
Dima_G
Цитата(Flexz @ Oct 15 2009, 11:12) *
Это элементарно контролируется сигнатурой и/или sizeof.

И вообще меня неправильно поняли - я не предлагаю тащить в С концепции С++, а лишь провожу аналогии - это все там и так есть, просто приглядитесь повнимательнее.


Сигнатура - проверка run-tmie + занимает ресурсы.
А про sizeof - не понял как его можно применять для котнтроля типа того, что нам передали по void указателю unsure.gif

Так что не убедили sad.gif
Нет в Си альтернативы С++ому контролю типов.

PS Откомпилируйте следующий пример Си компилятором и С++ компилятором. Почувствуйте разницу в строгости комиплятора

Код
#include <stdio.h>

///@brief Overflow in initialization
char acBuf_[3] = "12345678";            // !!! Error in C++, Warinig in C

///@brief Function which change argument's value
void TestFun(int* piNum_)
{
(*piNum_) = 15;
}

////////////////////////////////////////
int main()
{
  volatile const int NUM = 1;
  TestFun(&NUM);                        // !!! Error in C++, Warinig in C

  printf("Num = %i\n", NUM);
  return 0;
}
Flexz
По теме C vs C++ извините, но.. даже отвечать не хочу. На эту тему сломано столько копий, мама негорюй. Cам я использую все варианты - С, С++ и С# в зависимости от того что считаю лучшим для конкретной задачи (остновным критерием часто оказывается наличие в распоряжении готового кода). И ни спорить ни убеждать вас я ни в чем не собираюсь.

Но на тему комента "// !!! Error in C++, Warinig in C" все же скажу "А не надо писать так, что бы оставались ворнинги, ни в С ни в С++"- вот поспорьте лучше с этим утверждением. Хотя каюсь - сам грешу smile.gif
SasaVitebsk
Если бы изначально всё продумывалось, то такого бы не получилось, но вы же знаете как всё происходит...

1. Сделали велосипед...
2. Что-то не устойчив на дороге - прикрутили ещё 2 колеса...
3. Руль тяжеловато крутится - поставили баранку...
4. Крутить педали не хочется - поставили двигатель...
...
10. На голову капает.....
...

35. Ба.... да у нас машина получилась, правда чё-то корявая...

smile.gif
zltigo
Цитата(Dima_G @ Oct 15 2009, 11:42) *
Откомпилируйте следующий пример Си компилятором и С++ компилятором. Почувствуйте разницу в строгости комиплятора

Я, конечно, дико извиняюсь, но трудно привести пример еще более дурацкого "преимущества" sad.gif, нежели Error/Warning. А если еще знать, что любым (по крайней мере я не знаю, каким нельзя) компилятором можно управлять уровнем Warning->Error. И вообще, нежели кто-то Warning-и отключает/игнорирует, то он сам себе Буратино sad.gif.
Цитата
Нет в Си альтернативы С++ому контролю типов

Ой, у современных 'C' компиляторов вполне разумно-сбалансированый контроль типов и то, что в C++ добавили немного паранои, типа
"а чего это вы тут void * пытаетесь инициализировать char *" ни есть ни особо плохо и ни особо хорошо.
Ну с этим
Код
#include <stdio.h>
char acBuf_[3] = "12345678";            // !!! Error in C++, Warinig in C

соль совсем в другом месте, а именно в случае:
Код
char acBuf_[3] = "123";

И
Код
int x = '1234';
demiurg_spb
Цитата(Dima_G @ Oct 15 2009, 12:42) *
Код
volatile const int NUM = 1;
Поделитесь пожалуйста сакральными знаниями, когда const может быть ещё и volatile?
_Pasha
Цитата(SasaVitebsk @ Oct 14 2009, 21:34) *
Если были групповые общие объявления, то я их делал в main, а в других объявлял как extern.

Я объявлял их там, где больше всего идет работа с ними. В остальных местах - extern. Если же такого факта не наблюдается, и частота использования переменных в нескольких местах примерно одинакова, то это выползают недостатки построения модулей. Тут словами делу не поможешь...
dxp
Цитата(demiurg_spb @ Oct 15 2009, 22:42) *
Поделитесь пожалуйста сакральными знаниями, когда const может быть ещё и volatile?

Что вас так удивляет? Представьте себе регистр только для чтения - регистр специальных функций (SFR) процессора: например, регистр данных приемника UART'а. Чтение из него сбрасывает флаг прерывания от приемника. Т.е. чтение надо делать обязательно. Писать нельзя. Вот вам и const volatile. Обычная вещь.
sigmaN
Все на столько привыкли к препроцессору, что видимо призадумались.
И я в том числе smile.gif
Dog Pawlowa
Структуры данных и передача указателей на структуры существенно помогают даже при их глобальности.
Я тоже широко использую препроцессор, но тут он бессилен что-нибудь сломать smile.gif
demiurg_spb
Цитата(dxp @ Oct 16 2009, 07:08) *
Что вас так удивляет?
Меня удивляет, что в приведённом примере был не регистр...
Цитата
Представьте себе регистр только для чтения - регистр специальных функций (SFR) процессора...
Да, за этот наглядный пример спасибо. А то меня как-то замкнуло на том, что const это константа и всё (т.е. её нельзя изменить и сама собой она не поменяется, а volatile тут сильно всё меняет).
Dima_G
Цитата(demiurg_spb @ Oct 17 2009, 00:25) *
Меня удивляет, что в приведённом примере был не регистр...
Да, за этот наглядный пример спасибо. А то меня как-то замкнуло на том, что const это константа и всё (т.е. её нельзя изменить и сама собой она не поменяется, а volatile тут сильно всё меняет).


Эта запись не запрещает меняться этой переменной. Она всего лишь запрещает ее менять в этой программе biggrin.gif

А вообще - написал volatile, тк иначе компилятор оптимизировал обращение к переменной в printf
sigmaN
Я вот тут призадумался...
А как бы нам так объявить volatile const, чтобы получился тот-же эффект как от
#define XINTCNF2 ( *(volatile unsigned long *)(0x0B34) )

P.S. Оно то понятно, что можно привлечь линкер и соответствующую #pragma для указания адреса.
Но как-то задефайнить привычнее и проще(даже тупо по кол-ву строк на одно объявление)
_Pasha
Цитата(sigmaN @ Oct 17 2009, 15:48) *
Но как-то задефайнить привычнее и проще(даже тупо по кол-ву строк на одно объявление)

Еще и переносимее, т.к. #pragma компилерозависимая
Rst7
Цитата
Я вот тут призадумался...
А как бы нам так объявить volatile const, чтобы получился тот-же эффект как от


Тю smile.gif Да все так же:

Код
#define XINTCNF2 ( *(volatile unsigned long const *)(0x0B34) )

unsigned long foo(void)
{
  XINTCNF2=1; //Error[Pe137]: expression must be a modifiable lvalue
  return XINTCNF2; //OK
}
sigmaN
Цитата
ю smile.gif Да все так же:

Ну я имел ввиду без использования препроцессора. "По правильному" smile.gif
Rst7
Цитата
Ну я имел ввиду без использования препроцессора.


Ах простите, я не понял. В общем случае должно быть так:

В сишном исходнике (или в .h-файле) надо написать extern volatile const int VAR;

В скрипте линкера определить символ VAR=1234.

Правда для некоторых архитектур при некоторых значениях адреса и в некоторых компиляторах эффективнее будет определение через #define. Например - ARM, адреса регистров периферии , IAR 5.x.
sigmaN
Цитата
Ах простите, я не понял.
Ну это не удивительно. Мы же не на форуме экстрасенсов wink.gif

Ну с линкером да, можно и через символ...Чё-т я не подумал сразу..
Хотя, #define по-моему выглядит со всех сторон привлекательнее.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.