Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум разработчиков электроники ELECTRONIX.ru _ Программирование _ Warning: #962-D: use of a type with no linkage to declare a function

Автор: Professor Chaos Aug 5 2018, 19:53

Простенький пример проекта из одного main.cpp файла

Код
// Файл main.cpp
// Тип состояния флага события
typedef volatile enum {
  FLAG_STATE_OFF         = 0,    // Флаг события сброшен
  FLAG_STATE_ON            = 1    // Флаг события взведён
} Flag_State_t;

// Получить значение флага события по указателю на него
Flag_State_t GetFlagState (const Flag_State_t *Flag_ptr) {return *Flag_ptr;}

// Установить заданное значение флага события по указателю на него
void SetFlagState (Flag_State_t *Flag_ptr,Flag_State_t NewFlsgState) {
  *Flag_ptr=NewFlsgState;
};

// Глобальные объекты - флаги событий
Flag_State_t MyFlag1, MyFlag2;                        



int main () {    
    
  SetFlagState (&MyFlag1,FLAG_STATE_ON);  // MyFlag1=FLAG_STATE_ON;
  MyFlag2=GetFlagState (&MyFlag1);             // MyFlag2=MyFlag1;
  while(1);
}


Компиляция выдает 2 предупреждения на функции GetFlagState и SetFlagState :
Цитата
warning: #962-D: use of a type with no linkage to declare a function

Если в объявлении типа Flag_State_t убрать volatile - предупреждения исчезнут.
Вопрос: о чём предупреждает Кейл? Чем ему не нравится volatile в определении типа Flag_State_t ?

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

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

Автор: Forger Aug 5 2018, 21:09

Цитата(Professor Chaos @ Aug 5 2018, 22:53) *
Если в объявлении типа Flag_State_t убрать volatile - предупреждения исчезнут.


Все правильно ругается компилятор, ведь typedef volatile можно применить лишь к типам данных: переменные, структуры и классы, а enum к ним не относится.
По сути enum - это что-то вроде #define, "область деятельности" препроцессора, но не самого компилятора как такового. Это если грубо.
По сути volatile - это некое "указание" компилятору что можно делать, а что нет с некоторым объектом данных или объектом указанного типа данных (например: typedef volatile struct).



От себя добавлю: лучше использовать volatile непосредственно при объявлении/создании объекта (переменной), а не вставлять его при объявлении типа данных.
Это позволяет дать возможность компилятору оптимизировать те куски кода, где это действительно нужно, а точнее - только с теми объектами, которые явно указаны, что они volatile.
В противном случае этот typedef с "внедренным" volatile может оказаться в коде там, где volitile окажется даже вредным. Явно это не увидите.

"В довесок": лучше вообще избегать глобальных переменных (поверьте, это вполне реально), тогда и volatile вообще не будет нужен sm.gif

ps Принято указывать версию компилятора и версию самой среды. Хотя в данном случае это не важно, т.к. речь про неправильное применение компилятора как такового.

Автор: VladislavS Aug 6 2018, 02:45

Добавлю лишь, что в C++ для объявления типа перечисления не нужен typedef. Вот так будет достаточно.

Код
enum Flag_State_t {
  FLAG_STATE_OFF  = 0,    // Флаг события сброшен
  FLAG_STATE_ON    = 1    // Флаг события взведён
};

Автор: jcxz Aug 6 2018, 04:48

Цитата(Forger @ Aug 6 2018, 00:09) *
"В довесок": лучше вообще избегать глобальных переменных (поверьте, это вполне реально), тогда и volatile вообще не будет нужен sm.gif

Глобальность или неглобальность вообще не имеет никакого отношения к volatile. И, естественно, ограничение области видимости переменной никак не поможет при многопоточном обращении к ней. Путаете тёплое с круглым.
А исходный пост конечно совершенно безграмотный.....

Автор: Arlleex Aug 6 2018, 04:54

Цитата(VladislavS @ Aug 6 2018, 06:45) *
Добавлю лишь, что в C++ для объявления типа перечисления не нужен typedef. Вот так будет достаточно.
Код
enum Flag_State_t {
  FLAG_STATE_OFF  = 0,    // Флаг события сброшен
  FLAG_STATE_ON    = 1    // Флаг события взведён
};

А может и нужен. Не знаю как в C++, а в Си typedef может быть полезен так же, как и в остальных случаях - чтобы не писать везде enum (type) при каждом объявлении переменной перечислимого типа.
Но зачем вообще что-то возвращать по указателю как выше и установку тоже по указателю, ума не приложу...

Автор: Forger Aug 6 2018, 05:09

Цитата(jcxz @ Aug 6 2018, 07:48) *
Глобальность или неглобальность вообще не имеет никакого отношения к volatile. И, естественно, ограничение области видимости переменной никак не поможет при многопоточном обращении к ней.

Имелось ввиду, что можно полностью избежать применения volatile, отказавшись от совместного (пусть даже и не глобального) прямого обращения к одним и тем же объектам.
Но к данной теме это действительно не имеет отношения, тут совсем другой случай ))

Автор: Сергей Борщ Aug 6 2018, 05:48

QUOTE (Professor Chaos @ Aug 5 2018, 22:53) *
Вопрос: о чём предупреждает Кейл? Чем ему не нравится volatile в определении типа Flag_State_t ?
Ему не нрвится передача volatile enum качестве параметра NewFlsgState и возврат результата в виде volatile enum. Подумайте сами - что означает volatile и как его компилятор должен трактовать в типе параметра и возвращаемого значения? Фактически вам volatile нужен только для MyFlag1 и MyFlag2, поэтому уберите volatile из объявления перечисления и добавьте к объявлению MyFlag1 и MyFlag2. Этим вы заодно дадите компилятору свободу оптимизировать весь остальной код, не связанный с непосредственным обращением к MyFlag1 и MyFlag2.

Автор: jcxz Aug 6 2018, 06:49

Цитата(Arlleex @ Aug 6 2018, 07:54) *
А может и нужен. Не знаю как в C++, а в Си typedef может быть полезен так же, как и в остальных случаях - чтобы не писать везде enum (type) при каждом объявлении переменной перечислимого типа.

В си++ как и в случае со структурами/классами, так и с enum - писать слово "enum" при объявлении переменной не обязательно. Вместе с именем объявленного типа компилятор сохраняет и информацию о том что это: структура-ли, enum-ли, ....
Вообще не понимаю: си++ уже седой весь и мхом порос с этими правилами, ещё несколько десятилетий назад, когда я его изучал, всё это уже было в нём, но почему-то народ упорно продолжает лепить где ни попадя typedef.... Какой смысл? Или работодатель за кол-во знаков в исходнике платит??? wacko.gif typedef в си++ мало где необходим. Но упорство просто какое-то фанатическое.... wacko.gif

Цитата(Arlleex @ Aug 6 2018, 07:54) *
Но зачем вообще что-то возвращать по указателю как выше и установку тоже по указателю, ума не приложу...

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

Автор: VladislavS Aug 6 2018, 07:02

А если ещё вспомнить, что typedef уже заменили на using sm.gif

PS: Краткость - сестра таланта.

Код
volatile bool MyFlag1, MyFlag2;                        

int main () {    
  MyFlag1 = true;
  MyFlag2 = MyFlag1;
  for(;;);
}

Автор: Arlleex Aug 6 2018, 07:46

Цитата(jcxz @ Aug 6 2018, 10:49) *
В си++ как и в случае со структурами/классами, так и с enum - писать слово "enum" при объявлении переменной не обязательно. Вместе с именем объявленного типа компилятор сохраняет и информацию о том что это: структура-ли, enum-ли, ....
Какой смысл? Или работодатель за кол-во знаков в исходнике платит???

Я и говорю, что в чистом Си оно полезно; как в C++ дела обстоят не знал, поскольку изучал его на уровне университетской программы еще.
А вот сохранять информацию о том, какой объект (структура, объединение и т.д.) лежит за идентификатором типа - ИМХО, могли еще в Си сделать, но не сделали wacko.gif

Автор: Forger Aug 6 2018, 10:05

Цитата(VladislavS @ Aug 6 2018, 10:02) *
А если ещё вспомнить, что typedef уже заменили на using sm.gif

Он есть начиная лишь с C++11. В штатном компиляторе кейла V5 нужно в опциях компилятора добавлять соотв. ключик "--cpp11", а в V6 это уже не требуется.

Считаю using крайне полезной штукой! А точнее, тут более полезно наличие знака "=", именно он делает запись очень понятной с ходу, а не как это идет в typedef:
Вот для примера сравните две записи:
Код
typedef unsigned long int MyType;

Код
using MyType = unsigned long int;



Если же этих записей using много, то код становится еще более читаемым и понятным.
Вот пример из моего кода ("OS wrapper for cmsis_os2 (Keil RTX)"):

Код
    
class AbstractThread
{
public:
    using Handle       = osThreadId_t;
    using ControlBlock = osRtxThread_t;
    using Body         = osThreadFunc_t;
    using Priority     = osPriority_t;
    using StackItem    = uint64_t;
    using StackSize    = uint32_t;
    using NotifyValue  = uint32_t;
    using TimeMs       = uint32_t;
...

Автор: VladislavS Aug 6 2018, 10:23

Форма записи это всё шелуха и вкусовщина. Главное в using это умение работать с шаблонами.

Код
template<typename T> using add_t = typename add_template<T>::type;


Теперь можно вместо typename add_template<T>::type использовать add_t<T>

Автор: Professor Chaos Aug 6 2018, 18:31

Цитата(Forger @ Aug 6 2018, 00:09) *
От себя добавлю: лучше использовать volatile непосредственно при объявлении/создании объекта (переменной), а не вставлять его при объявлении типа данных.
Это позволяет дать возможность компилятору оптимизировать те куски кода, где это действительно нужно, а точнее - только с теми объектами, которые явно указаны, что они volatile.
В противном случае этот typedef с "внедренным" volatile может оказаться в коде там, где volitile окажется даже вредным. Явно это не увидите.


Спасибо. Действительно так и есть. Если убрать volatile из объявления типа и вставлять его только перед требующими того ОБЪЕКТАМИ, то предупреждения исчезают.



Цитата(Сергей Борщ @ Aug 6 2018, 08:48) *
Ему не нрвится передача volatile enum качестве параметра NewFlsgState и возврат результата в виде volatile enum. Подумайте сами - что означает volatile и как его компилятор должен трактовать в типе параметра и возвращаемого значения? Фактически вам volatile нужен только для MyFlag1 и MyFlag2, поэтому уберите volatile из объявления перечисления и добавьте к объявлению MyFlag1 и MyFlag2. Этим вы заодно дадите компилятору свободу оптимизировать весь остальной код, не связанный с непосредственным обращением к MyFlag1 и MyFlag2.


Тоже согласен.
НО! Вот исправленный код, на который у компилятора нет предупреждений. Обратите внимание, что параметры функций имеют спецификатор volatile. Без них компилятор выдаёт ошибки о несоответствии типов аргументов декларированных и переданных.
Код
// Тип состояния флага события
typedef enum  {
  FLAG_STATE_OFF      = 0,                                    // Флаг события сброшен
  FLAG_STATE_ON       = 1                                        // Флаг события взведён
} Flag_State_t;

// Получить значение флага события по указателю на него
Flag_State_t GetFlagState (volatile Flag_State_t *Flag_ptr) {return *Flag_ptr;}

// Установить заданное значение флага события по указателю на него
void SetFlagState (volatile Flag_State_t *Flag_ptr,Flag_State_t NewFlagState) {
  *Flag_ptr=NewFlagState;
};


// Глобальные объекты - флаги событий
volatile Flag_State_t MyFlag1, MyFlag2;                        


int main (){    
    
  SetFlagState (&MyFlag1,FLAG_STATE_ON);  // MyFlag1=FLAG_STATE_ON;
  MyFlag2=GetFlagState (&MyFlag1);        // MyFlag2=MyFlag1;
  while(1);
}


А если перед типом возвращаемого значения функции GetFlagState добавить volatile, то получим другое предупреждение компилятора: warning: #815-D: type qualifier on return type is meaningless
Это уже как раз то, о чём вы сказали.

Вообще, из 10 ответов только 2 реально по теме. КПД 20%

Для остальных 80%.
Да, код убогий, глупый и никчёмный. Не имеющий никакого практического смысла и применения. Но!!! Задача его была показать суть проблемы, написав при этом минимум строк кода. Чтобы не отвлекать отвечающих СТРОГО ПО-СУЩЕСТВУ ЗАДАННОГО ВОПРОСА на второстепенные детали. И двое из десяти это поняли, и ответили действительно по-существу.

Автор: Forger Aug 6 2018, 18:37

Цитата(Professor Chaos @ Aug 6 2018, 21:31) *
Вообще, из 10 ответов только 2 реально по теме. КПД 20%

Так это был тест??? blink.gif

Автор: Professor Chaos Aug 6 2018, 19:57

Цитата(Forger @ Aug 6 2018, 21:37) *
Так это был тест??? blink.gif

Это был вопрос по-существу. На который нужен был ответ по-существу.
Но количество таких ответов, относительно общего количества ответов, можно расценивать и как результат некоего спонтанного теста biggrin.gif

Автор: Forger Aug 6 2018, 20:14

Цитата(Professor Chaos @ Aug 6 2018, 22:57) *
Это был вопрос по-существу. На который нужен был ответ по-существу.

Тут все ТС считают, что их вопросы - вопросы по-существу, и всем соотв. нужны аналогичные ответы ...
Но на практике не все так очевидно wink.gif

Автор: Сергей Борщ Aug 7 2018, 00:27

QUOTE (Professor Chaos @ Aug 6 2018, 21:31) *
Вот исправленный код, на который у компилятора нет предупреждений. Обратите внимание, что параметры функций имеют спецификатор volatile.
Нет. Сам параметр не имеет квалификатора volatile. Этот квалификатор имеет переменная, на которую этот параметр указывает. Обратите внимание, что volatile Flag_State_t *Flag_ptr, Flag_State_t *volatile Flag_ptr, volatile Flag_State_t * volatile Flag_ptr и Flag_State_t *Flag_ptr - четыре совершенно разные сущности.

Автор: VladislavS Aug 7 2018, 01:41

Цитата(Professor Chaos @ Aug 6 2018, 21:31) *
СТРОГО ПО-СУЩЕСТВУ ЗАДАННОГО ВОПРОСА
Строго по существу - в вашем примере volatile вообще нигде не нужен! Но мы люди опытные и понимаем, что дальше вы наговнокодите прерывание, которое флаги будет менять. Поэтому и посоветовали поставить volatile в единственно верное место - определение переменных MyFlag.
Дожились, каждый говнокодер ещё и поучать будет...

Автор: Professor Chaos Aug 7 2018, 02:35

Цитата(VladislavS @ Aug 7 2018, 04:41) *
в вашем примере volatile вообще нигде не нужен! Но мы люди опытные и понимаем, что дальше вы наговнокодите прерывание, которое флаги будет менять.

В своём первом сообщении я об этом прямо сказал, когда объяснял, зачем мне нужен volatile. Догадываться не надо - всё написано.

Автор: VladislavS Aug 7 2018, 02:59

Не надо нас лечить, лучше принимайте советы к исполнению.

Автор: jcxz Aug 7 2018, 06:07

Цитата(Professor Chaos @ Aug 6 2018, 22:57) *
Но количество таких ответов, относительно общего количества ответов, можно расценивать и как результат некоего спонтанного теста biggrin.gif

Очередной деятель которому все должны.....

Автор: VladislavS Aug 7 2018, 06:24

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

Автор: Forger Aug 7 2018, 07:31

Мне одному кажется, что эта тема к Keil как таковому практически не имеет отношения?
Уверен, что тут она была бы более уместной: http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showforum=192

Автор: IgorKossak Aug 7 2018, 18:44

Цитата(Forger @ Aug 7 2018, 10:31) *
Мне одному кажется, что эта тема к Keil как таковому практически не имеет отношения?
Уверен, что тут она была бы более уместной: http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showforum=192

Согласен. Перенёс.
Модератор.

Автор: Professor Chaos Aug 7 2018, 19:33

Цитата(VladislavS @ Aug 7 2018, 09:24) *
Хотел промолчать, но не буду. У человека, который с помощью функции по указателю осуществляет из прерывания доступ к глобальной булевой по сути переменной большие проблемы. Это в будущем первый кандидат в секту "пресвятого инлайна".

Ещё раз повторяю, это ПРИМЕР ДЛЯ ДЕМОНСТРАЦИИ ЭФФЕКТА, а не код реального проекта.
Вот более приближенный к реальности код (ещё до исправления ошибки)
Код
// Тип состояния флага события
typedef volatile enum  {
  FLAG_STATE_OFF      = 0,                                    // Флаг события сброшен
  FLAG_STATE_ON       = 1                                        // Флаг события взведён
} Flag_State_t;

// Клас с флагом события
class SomeClass {
    static Flag_State_t Flag;   // Флаг некоторого события
    // Далее идут прочие приватные члены класса
    // ...
  public:
    static void SetFlag (Flag_State_t NewState) {Flag=NewState;}
    static Flag_State_t GetFlag () {return Flag;}
    // Далее идут прочие публичные члены класса
    // ...
};

// Обработчик какого-то прерывания
void Some_IRQ_Handler () {
  // Делаем что-то
  // ...
  SomeClass::SetFlag (FLAG_STATE_ON); // Взводим флаг события
  // Делаем что-то
  // ...
}

// Статические данные-члены класса SomeClass
Flag_State_t SomeClass::Flag=FLAG_STATE_OFF;                        

int main (){    
    // Создаём объект и указатель на него
  SomeClass *Obj_p=new SomeClass;  
  // Делаем что-то
  // ...
  // Ждем наступления события
  while (Obj_p->GetFlag ()==FLAG_STATE_OFF);
  Obj_p->SetFlag (FLAG_STATE_OFF);  // Сбрасываем флаг
  // Событие произошло. Делаем что-то
  // ...
  delete Obj_p;     // Разрушаем объект по указателю на него
  while(1);
}


Спасибо всем, кто отвечал по-существу. Понял свою ошибку, прошу прощения, исправлюсь, больше так не буду

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)