Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: typeinfo + IAR v.5.11B
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
alux
Редактирование переменных типа double реализовал при помощи виртуальных функций. Какую именно переменную редактировать указывается при помощи указателя pValue, в которую передается адрес переменной :
Код
volatile double* pValue;            // Указатель на редактируемый параметр
volatile double __eeprom* pValueEE; // Указатель на редактируемый параметр в EEPROM
volatile double* param[] = {&area, &Kt, &Y0};
volatile double __eeprom* paramEE[] = {&ee_area, &ee_Kt, &ee_Y0};
..................
class TEditValue
{
public:
  void Up()       // k_right
  {

       if(pValue-- == *param) pValue = *(param+2);
       if(pValueEE-- == *paramEE) pValueEE = *(paramEE+2);
       ......................................... // какие-то действия при нажатии кнопки UP
       ftoa(*pValue, lcd_buf, 3, 0);
  }
  
  void Down()     // k_left
  {
      .................
  }
  
  void Right()    // k_enter
  {
     *pValue = round_to_pow(atof(lcd_buf), -3);  // округлить до 3 знака после запятой
     *pValueEE = *pValue;       // сохранить отредактированную переменную в EEPROM
  }
  
  void Numeric()  // 0...9
  {
          ..............
   }
};

// Функция, вызываемая при входе в пункт меню
void Level1Item1Sub1sub1_Func(void)
{
  TCritSect cs;
  
  pValue = *param;        // &area;
  pValueEE = *paramEE;    // &ee_area;
.............
}
Это все работает. Но теперь возникла необходимость реализовать почти те же действия для переменных типа unsigned char. Первое, что пришло на ум, использовать указатель void* pValue. Когда необходимо редактировать переменную типа unsigned char (при входе в соответствующий пункт меню), то указатель приводится к типу (unsigned char*).
Но возникает вопрос, как привести к конкретному типу указатель pValue в реализации виртуальной функции? Т.е. определение типа происходит в runtime. В C++ есть механизм динамического определения типа во время выполнения программы при помощи операции typeid и класса type_info. В IAR v.5.11B есть даже соответствующий заголовочный файл <typeinfo>. Но при использовании ее компилятор выдает ошибки

Error[Pe878]: Embedded C++ does not support run-time type information C:\Program Files\IAR Systems\Embedded Workbench 5.0\avr\INC\DLIB\typeinfo 62
Error[Pe020]: identifier "typeid" is undefined D:\Sasha\MyWork\IAR projects\device\include\tasks.h 351

Для чего нужен этот файл, если нельзя им пользоваться? И как мне решить проблему с редактированием разных типов переменных посредством одного указателя?

PS. Создание второго класса для редактирования переменных unsigned char типа не предлагать.
KRS
typeid же только на классы с виртуальными функциями действует!
alux
Цитата(KRS @ Jun 18 2008, 11:48) *
typeid же только на классы с виртуальными функциями действует!
Прежде не имел дела с typeid. Просто хочу разобраться... Вот цитата:
Цитата
Оператор typeid допустимо использовать с выражениями и именами любых типов. Например, его операндами могут быть выражения встроенных типов и константы. Если операнд не принадлежит к типу класса, то typeid просто возвращает его тип:

int iobj;
cout << typeid( iobj ).name() << endl; // ia?aoaaony: int
cout << typeid( 8.16 ).name() <<endl; // печатается: double
KRS
Цитата
Следует помнить, что RTTI в собственном смысле, как динамическое распознавание типа, работает только с полиморфными типами, т. е. классами, имеющими хотя бы одну виртуальную функцию. Если применить операцию typeid к обычному типу, идентификация типа будет произведена статически, т. е. при компиляции.
alux
Понятно. Значит typeid мне не подходит. Тогда все же буду использовать указатель void* pValue, и в виртуальной функции в зависимости от размера указателя (или содержимого указателя) буду выполнять действия либо для double, либо для unsigned char. Но есть маленькая проблема. Раньше (с использованием явного указателя типа double*) ограничение выхода за пределы массива осуществлялось проверкой:
Код
  void Up()       // k_right
  {  
    if(pValue-- == *param) pValue = *(param+2);
    if(pValueEE-- == *paramEE) pValueEE = *(paramEE+2);
.....................
Теперь, когда pValue имеет тип void*, и ему присвоен адрес переменной (*param = &area), компилятор выдает ошибку на эти строки:
Код
Error[Pe137]: expression must be a modifiable lvalue ...353
Не помогает в операции сравнения и постдекремента явное приведение указателя к типу (double*)
Код
if(((double*)pValue)-- == *param) (double*)pValue = *(param+2);
. Как мне удовлетворить компилятор?
KRS
операции с указателями void* запрещены, потому что не известен размер объекта на которые указывает указатель.

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


Цитата(alux @ Jun 18 2008, 16:39) *
Не помогает в операции сравнения и явное приведение указателя к типу (double*)
Код
if(((double*)pValue)-- == *param) (double*)pValue = *(param+2);

. Как мне удовлетворить компилятор?


при преобразовании указателя в (double*) значение его преобразуется, но результат -- обратно в указатель записать нельзя!
можно написать так
Код
if(((double*)pValue)-- == *param) pValue = *(param+2);
else pValue = ((double*)pValue) - 1;
alux
Цитата(KRS @ Jun 18 2008, 15:58) *
можно написать так
Код
if(((double*)pValue)-- == *param) pValue = *(param+2);
else pValue = ((double*)pValue) - 1;
Та же ошибка на первую строчку (в условии).
KRS
Цитата(alux @ Jun 18 2008, 17:08) *
Та же ошибка на первую строчку (в условии).

Код
if(((double*)pValue) == *param) pValue = *(param+2);
else pValue = ((double*)pValue) - 1;


-- забыл убрать
alux
Получается, врут учебники:
Цитата
Для указателя на объект неопределённого типа не существует способа непосредственной перенастройки указателя на следующий объект с помощью операции инкрементации. В операторе, реализующем операции инкрементации и декрементации, только с помощью операций явного преобразования типа можно сообщить транслятору величину, на которую требуется изменить первоначальное значение указателя.

pUndefPointer++; // Это неверно, инкрементация не определена…
(int *)pUndefPointer++; // И так тоже ничего не получается…
((int *)pUndefPointer)++; // А так хорошо… Сколько скобок!
++(int *)pUndefPointer; // И вот так тоже хорошо…
Я же вначале так и делал...

PS. И еще. Применима ли операция sizeof() к указателю типа void*, проинициализированному адресом переменной?
Код
if(sizeof(pValue) == sizeof(double*)) {...}


PS2. 2 moderator. Эту тему можно перенести в раздел Помощь начинающему.
Сергей Борщ
Цитата(alux @ Jun 18 2008, 16:48) *
Получается, врут учебники:
Получается, врут. Старые компиляторы пропускали, новые - уже нет. Касаемо именно указателя на void было отдельное ограничение в стандарте.
Цитата(alux @ Jun 18 2008, 16:48) *
PS. И еще. Применима ли операция sizeof() к указателю типа void*, проинициализированному адресом переменной?
Применима, но совершенно бессмысленна. Ибо указатель имеет одинаковый размер независимо от того, на что он указывает. Т.е. ваше сравнение будет давать истину для любого указателя, неважно на что он указывает.

В вашем случае просятся шаблоны.
alux
Цитата(Сергей Борщ @ Jun 18 2008, 17:19) *
Применима, но совершенно бессмысленна.
Я так и думал.
Цитата(Сергей Борщ @ Jun 18 2008, 17:19) *
В вашем случае просятся шаблоны.
Если бы реализация отличалась бы только типом данных... А то для переменных типа double используется функция ftoa, а для целочисленных itoa. Да и количество переменных может быть разным, что влечет за собой разную реализацию для переменных разных типов.
Получается, можно лишь передавать информацию о типе в качестве параметра... Или есть варианты?

PS. Я вижу выход только в использовании глобальной переменной bool real, с помощью которой передавать информацию о типе (double или unsigned char) и в реализации функции в зависимости от значения real выполнять нужные действия для конкретных типов..
Сергей Борщ
Цитата(alux @ Jun 18 2008, 17:44) *
Если бы реализация отличалась бы только типом данных... А то для переменных типа double используется функция ftoa, а для целочисленных itoa.
Можно написать две специализации шаблона для каждого из типов, можно ftoa и itoa "обернуть" в шаблон... Можно написать абстрактный базовый класс с виртуальной функцией ToString, и от него производные классы-шаблоны с параметрами double и char, а в вашей функции редактирования использовать эту виртуальную функцию.
Цитата(alux @ Jun 18 2008, 17:44) *
Да и количество переменных может быть разным, что влечет за собой разную реализацию для переменных разных типов.
Это недопонял.
IgorKossak
Цитата(alux @ Jun 18 2008, 17:44) *
...Да и количество переменных может быть разным, что влечет за собой разную реализацию для переменных разных типов.

Тоже решается перегрузкой методов класса.
alux
Цитата(Сергей Борщ @ Jun 19 2008, 00:21) *
Это недопонял.

Объясню с самого начала. Обработка нажатий клавиш должна обрабатываться по-разному в зависимости от текущего режима:
Код
//------------------------------------------------------------------------------
class TKey
{
public:
    TKey() { }
    virtual void Up()   = 0;  
    virtual void Down() = 0;
    virtual void Left() = 0;
    virtual void Right() = 0;
    virtual void Numeric() = 0;
};

//------------------------------------------------------------------------------
class TMainMenu : public TKey
{
public:
    void Up()     // k_right
    {
        SET_MENU(PREVIOUS);
    }

    void Down()   // k_left
    {
        SET_MENU(NEXT);      
    }
    void Left()   // k_esc
    {
        SET_MENU(PARENT);
    }
    
    void Right()  // k_enter
    {
        SET_MENU(SIBLING);
    }    
    
    void Numeric() // 0...9
    {
      TCritSect cs;
      ....................
    }
};
//------------------------------------------------------------------------------
class TEditTime : public TKey
{
public:   // Реализация для обработки времени
    void Up()  {...}
    void Down()  {...}
    void Left()  {...}
    void Right()  {...}
    void Numeric()  {...}
};
//------------------------------------------------------------------------------
class TEditValue : public TEditTime
{
public:   // Реализация для редактирования переменных
    void Up()  {...}
    void Down()  {...}
    void Left()  {...}
    void Right()  {...}
    void Numeric()  {...}
};

//===========================================================================
//  Процесс сканирования клавиатуры
//---------------------------------------------------------------------------
OS_PROCESS void TKeyScan::Exec()    //TProc1
{
  for(;;)
  {
     PCInt3.Wait();
    
    Sleep(20);        // Задержка 10*2=20мсек для устранения дребезга контактов
    
    scan_key();
  
    if(key_code.scan & KEY_PRESSED)
    {
      key_code.scan &= ~KEY_PRESSED;   // Clear MSB of scan_code (key_pressed)
    
      switch(key_code.scan)  
      {
         case UP:     //k_right
              CurrentMode->Up();  
              break;
              
         case DOWN:   //k_left
              CurrentMode->Down();
              break;
              
         case LEFT:   //k_esc
              CurrentMode->Left();
              break;
              
         case RIGHT:  //k_enter
              CurrentMode->Right();
              break;
              
         default:     // 0...9
              CurrentMode->Numeric();
              break;
      }
    }
  }
}
В виртуальных функциях класса EditValue обрабатываются глобальные переменные, которые передаются в функцию через указатель (см. пост# 1). При входе в меню "Параметры" нужно редактировать три переменные типа double. На ЖКИ они отображаются на первых трех строках. Кнопками UP, DOWN осуществляется выбор переменной для редактирования. В функции Up():
Код
.......................................
(y == 1) ? y = 21 : y -= 10;  // y - координата
.......................................

В функции Down():
Код
(y == 21) ? y = 1 : y += 10;

В Right():
Код
*(double*)pValue = round_to_pow(atof(lcd_buf), -3);
*(double __eeprom*)pValueEE = *(double*)pValue;//сохранить в EEPROM

А теперь при входе в пункт меню "Опции" нужно отредактировать две переменные типа unsigned char. Для этого создаю глобально
Код
volatile unsigned char* option[] = {&rate, &nmax};
volatile unsigned char __eeprom* optionEE[] = {&ee_rate, &ee_nmax};
и при входе в п. меню
Код
  pValue = *option;        // &rate;
  pValueEE = *optionEE;    // &ee_rate;

Поэтому, при нажатии UP:
Код
(y == 11) ? y = 21 : y -= 10;

при нажатии DOWN:
Код
(y == 21) ? y = 11 : y += 10;

при нажатии RIGHT:
Код
*(unsigned char*)pValue = atoi(lcd_buf, (unsigned char*)pValue);
*(unsigned char __eeprom*)pValueEE = *(unsigned char*)pValue; //сохранить в EEPROM

т.е. эти переменные располагаются на экране на второй и третьей строке.
Цитата(IgorKossak @ Jun 19 2008, 07:58) *
Тоже решается перегрузкой методов класса.

Т.е. передавать параметры во все виртуальные функции? Было бы проще создать новый класс EditValueChar со своими виртуальными функциями Up(), Down(), Left(), Right(), Numeric(). Это мне не подходит...
Я реализовал так как говорил выше в предыдущем посте, - через глобальную переменную bool real, а в виртуальных методах в зависимости от его значения выполняю нужные манипуляции с переменными разных типов.
alux
Прошу помощь зала smile.gif

С редактированием и сохранением переменных типа double проблем нет. А вот с переменными типа unsigned char какая-то ерунда получается... Напомню, что есть две переменных unsigned char rate, nmax и две переменные unsigned char ee_rate, ee_nmax. Их адреса содержатся в глобальных массивах:
Код
unsigned char* option[] = {&rate, &nmax};
unsigned char __eeprom* optionEE[] = {&ee_rate, &ee_nmax};

В начале программы
Код
int main()
{
  // Initialise variables from EEPROM
  area = ee_area;        
  Kt = ee_Kt;          
  Y0 = ee_Y0;          
  rate = ee_rate;      
  nmax = ee_nmax;      
...........

При входе в пункт меню
Код
//  Редактирование установок прибора:
//  значения частоты АЦП (rate) и значения усреднения (nmax)
//------------------------------------------------------------------------------
void Level1Item1Sub2sub1_Func(void)
{
  TCritSect cs;
  
  real = false;
  
  pValue = *option;        // &rate;
  pValueEE = *optionEE;    // &ee_rate;

  ks0108GotoXY(94, 21);
  itoa(nmax, lcd_buf);
  ks0108PutStr(lcd_buf);
  
  ks0108GotoXY(94, 11);
  itoa(*(unsigned char*)pValue, lcd_buf);
  ks0108PutStr(lcd_buf);
  
  xx = 94;
  yy = 11;

  plcd_buf = lcd_buf;
  w = ks0108CharWidth(*lcd_buf)-2;  
  CurrentMode = &EditValue;
  
ks0108FillRect(0, 40, 20, 9, WHITE);                    // Эти три строки нужны только для посмотреть
ks0108GotoXY(0, 40);                                         //
ks0108PutChar((char)(*((unsigned char*)pValue+0))+'0'); //  содержимое указателя + 0, +1
}
Обе переменные сохранены в EEPROM (имеют значения 1 и 2 соответственно). Это можна увидеть по прочитанному hex-файлу EEPROM (последние два байта):
Код
:020000020000FC
:100000000000A03F829523405D8F52400102FFFF18
При входе в пункт меню вывожу содержимое указателя на экран
Код
ks0108PutChar((char)(*((unsigned char*)pValue+0))+'0');
выводит '1'.
А вот если вывести
Код
ks0108PutChar((char)(*((unsigned char*)pValue+1))+'0');
, то выводит '0'. Получается, что не правильно смещает указатель
Код
pValue = (unsigned char*)pValue + 1;


Делаю то же самое с переменными double - все в порядке, выводит то, что положено. В чем дело? 05.gif


PS. Какой-то абсурд! На всякий случай проверил в симуляторе этот кусочек:
Код
int main()
{
  // Initialise variables from EEPROM        
  rate = ee_rate;      
  nmax = ee_nmax;  
pValue = *option;        // &rate;
pValueEE = *optionEE;    // &ee_rate;
pValue = (unsigned char*)pValue+1;
pValue, как и положено, указывает на содержимое переменной nmax, инициализированной переменной ЕЕПРОМа ee_nmax = 2.
Затем, если вставить следующий кусок в main и вывести на индикатор значения массива unsigned char* option, который содержит адреса переменных unsigned char rate=1, nmax=2:
Код
int main()
{................
  lcd_Init();
  menu_Init();
pValue = *option;        // &rate;
ks0108FillRect(0, 40, 20, 9, WHITE);
ks0108GotoXY(0, 40);
ks0108PutChar((char)(*((unsigned char*)pValue+0))+'0');  
ks0108GotoXY(10, 40);
ks0108PutChar((char)(*((unsigned char*)pValue+1))+'0');
То выводит '1' и '0' (должно быть '1' и '2')

А если таким же образом выводить на индикатор содержимое массива double* param, который содержит адреса переменных double area=1.25, Kt=2.556, Y0=3.29:
Код
int main()
{................
  lcd_Init();
  menu_Init();
pValue = *param;        // &area;
ks0108FillRect(0, 40, 40, 9, WHITE);
ks0108GotoXY(0, 40);
ks0108PutChar((char)(*((double*)pValue+0))+'0');
ks0108GotoXY(10, 40);
ks0108PutChar((char)(*((double*)pValue+1))+'0');
ks0108GotoXY(20, 40);
ks0108PutChar((char)(*((double*)pValue+2))+'0');
То выводит '1', '2' и '3'? как и положено!!!
Как такое может быть?!! Почему не выводится содержимое unsigned char переменной через указатель со смещением (+1)? Объясните мне, пожалуйста. help.gif

PS2. Я пока вышел из положения так:
Код
  void Up()       // k_right
  {.......................
      if(pValue == *option) pValue = *(option+1);    
      else pValue = *option;                                      //pValue = (unsigned char*)pValue - 1;
  
  void Down()     // k_left
  {........................      
      if(pValue == *(option+1)) pValue = *option;
      else pValue = *(option+1);                                //pValue = (unsigned char*)pValue + 1;
Но это, понятное дело, работает только для массива из двух переменных.
Сергей Борщ
Цитата(alux @ Jun 19 2008, 16:38) *
Как такое может быть?!! Почему не выводится содержимое unsigned char переменной через указатель со смещением (+1)? Объясните мне, пожалуйста. help.gif
Полагаю, внимательное исследование листинга поможет. И зачем столько приведений типов?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.