Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

Един С въпрос
http://mcu-bg.com/mcu_site/viewtopic.php?f=3&t=506
Страница 1 от 2

Автор:  Цецо [ Сря Мар 23, 2005 3:15 pm ]
Заглавие:  Един С въпрос

Значи имам да пиша тестов софтуер за едни платки. Програмата се състои от около 100 тина теста, които не подлежат на някакво кое знае какво унифициране. Всеки ще е в отделна функция. Не са последователни, изпълняват се при команда от PC. Как предлагате да реализирам викането на функциите? Най-простото решение ми се струва SWITCH,CASE, ама 100 броя изглежда малко дървено. Някакво по хитро решение да се сещате?

Автор:  [ Сря Мар 23, 2005 3:41 pm ]
Заглавие: 

Ако компилатора на C, който ще използваш, поддържа указатели към функции, ще стане най-елегантно. Правиш един масив от указатели към функции, след което по кода на командата адресираш масива и се изпълнява съответната функция. Кодовете на командите трябва да са последователни числа в даден обхват, не е задължително да започват от нула. Например:
typedef bool (*ParseCommand)(void);
ParseCommand ParseCommandFunction [ NUMBER_OF_COMMAND ] =
{
ParseCommand1, ParseCommand2, ParseCommand3, ParseCommand4,ParseCommand5, ParseCommand6, ParseCommand7, ParseCommand8,ParseCommand9, ParseCommand10, ParseCommand11, ParseCommand12,ParseCommand13, ParseCommand14, ParseCommand15, ParseCommand16,ParseCommand17, ParseCommand18, ParseCommand19, ParseCommand20,
ParseCommand21, ParseCommand22, ParseCommand23,
#ifdef USE_LAMP_MONITORING
ParseCommand24, ParseCommand25, ParseCommand26,
#endif
#ifdef USE_PERCENT_BURN_OUT
ParseCommand27
#endif
};

После извикваш дадена функция така:
ParseCommandFunction[cmd.Buffer[5]-1]();

Преди обаче да извикаш дадена функция от масива, провери индекса на масива дали е в обхвата, иначе лошо :cry:
Цитирания фрагмент от програма е от работеща програма за TCP/IP мрежа, компилирана с Borland C++ 5.02 и работи безотказно под Windows XP 2-ра година.

Автор:  [ Сря Мар 23, 2005 3:48 pm ]
Заглавие: 

Хрумна и още нещо: ако кодовете на командите, получавани от ПК не са последователни числа, правиш си пак масива с функции и там където има непоследователни кодове слагаш някаква фиктивна функция, която няма да върши нищо:

ParseCommand ParseCommandFunction[ NUMBER_OF_COMMAND ] =
{
ParseCommand1, FicticiousFunction,FicticiousFunction,FicticiousFunction,
ParseCommand5,ParseCommand6, ParseCommand7, ParseCommand8,ParseCommand9, ParseCommand10, ParseCommand11, ParseCommand12,ParseCommand13, ParseCommand14, ParseCommand15, ParseCommand16,ParseCommand17, ParseCommand18, ParseCommand19, ParseCommand20,
ParseCommand21, ParseCommand22, ParseCommand23,
#ifdef USE_LAMP_MONITORING
ParseCommand24, ParseCommand25, ParseCommand26,
#endif
#ifdef USE_PERCENT_BURN_OUT
ParseCommand27
#endif
};

За код команда 2,3 и 4 се извикава фиктивната функция.

Автор:  bateAz [ Сря Мар 23, 2005 4:05 pm ]
Заглавие: 

С масив от указатели към финкциите. Това ще да е най-смисленото и най-прегледното показване на нещата. И най-лесно се коригира при нужда.

Автор:  Реконструктор [ Сря Мар 23, 2005 5:17 pm ]
Заглавие: 

По принцип, ако търсиш най-лесното, то то е да си наредиш ф-иите през реавно разстояние. Ако, разбира се, не пестиш място и компилатора ти го позволява. :)
С масива от указатели има един проблем, и той е, че когато отпадне някоя от ф-иите, в масива се образува дупка и той трябва да се преиндексира целия. За това има 2 подхода. Най-простия е ф-ята да се вика от ф-я посредник, която да проверява за дупки.
Код:
void CallFunction(short nIndex)
{
   if (arr_functions[nIndex] != NULL)
       arr_functions[nIndex]();
}


Другия начин е не масив, ами списък, който при повече желание може да се направи да имитира масив, но това май вече не е за тебе. :)
Добре е и да се измислят хитри макросчета, така, че адреса на ф-ята да се добавя към масива още при нейното декларириране, това ще опрости малко работата.

Автор:  [ Сря Мар 23, 2005 6:05 pm ]
Заглавие: 

Реконструктор, не ми стана ясно, как след като отпадне някоя функция от масива ще се изпълни условието за NULL във функцията посредник. Нали веднага компилатара ще замести нейната позиция със следващата функция в масива, освен ако на позицията на отпадналата функция в масива не запишеш NULL. Ами то след като ще записваш все пак нещо на тази позиция в масива (т.е. заемаш памет ), защо да не е адреса на фиктивната функция ? Дори у да объркаш нещо в индексите на масива ще се извика коректно фиктивната функция. Освен това е задължително във функцията посредник да правиш проверка за обхвата на nIndex, тъй като nIndex ще е променлива получавана от външния свят, т.е. проверката:

if (arr_functions[nIndex] != NULL)
arr_functions[nIndex]();

може и да не се изпълни коректно, ако nIndex е по-голям от максималния размер на масива и ще извикаш функция от произволно място в паметта, ако след края на масива в паметта имаш данни различни от NULL.
Според мен, ако ще се записва на мястото на липсваща команда в масива от функции NULL, проверката трябва да е следната:

#define MAX_ARRAY_OF_FUNCTION 10
void CallFunction(short nIndex)
{
if( nIndex < MAX_ARRAY_OF_FUNCTION &&
arr_functions[nIndex] != NULL )
arr_functions[nIndex]();
}

За списък е удобно, ама човека питаше за платки, следователно според мен става дума за едночипов микроконтолер и надали ще може да си позволи харченене на RAM за организиране на списък.

А и да задам и един глупав въпрос: Как го оцветявате програмния код, така че да се различава от останалия текст в съобщенията от форума ?

Автор:  Реконструктор [ Сря Мар 23, 2005 6:20 pm ]
Заглавие: 

Колега, обсъждаш незначителни подробности. Опитай се да адресираш несъществуваща ф-я, компилатора ще изгърми веднага. Та за това на мястото на адреса на тая несъществуваща ф-я слагаш NULL. Другия начин, както съм написал в предишния си пост, е да преиндексираш целия масив, като съответно трябва да съобразиш това със софтуера на ПЦ-то и изобщо е излишно напъване.

По повод на оцветяването - извършва се като натиснеш бутона Code, който се намира над полето, в което пишеш.

Автор:  [ Сря Мар 23, 2005 6:38 pm ]
Заглавие: 

Реконструктор, не се заяждам, но да бъдем точни и коректни в отговорите си ! По повод на "незначителните подробности" изпробвай следния код и виж какво ще стане.
Код:
typedef bool (*ParseCommand)(void);

bool Func1(void)   {   return true;   }

bool Func2(void)   {   return false;   }

bool Func3(void)   {   return true;   }

const int cNumberOfFunctions = 3;

ParseCommand ArrayOfFunctions[cNumberOfFunctions] =
{
   Func1, Func2, Func3

};

int main(int /*argc*/,char **/*argv*/)
{

int nIndex;
   // Извиквам в долния код в цикъл функциите от масива ArrayOfFunctions,
    // но умишлено допускам грешка с конструкцията nIndex < ( cNumberOfFunctions + 1 )
    // при което никой C/C++ няма да ви даде че имате грешка при компилация, т.е.
    // компилатора Ви няма да гръмне, а ще гръмне вашата програма по време
    // на изпълнение, защото ще извикате несъществуваща функция ! Компилаторите на
    // C/C++ не правят проверка за размерността на масивите когато ги индексирате,
    // Тази проверка си я прави програмиста, освен  ако масива не е реализиран
    // като class в C++, при което може да предефинирате оператори, които да
    // вършат тази работа.
   for( nIndex = 0; nIndex < (cNumberOfFunctions+1); nIndex++ )
      ArrayOfFunctions[nIndex]();

}

Автор:  Dimitar [ Сря Мар 23, 2005 8:45 pm ]
Заглавие: 

Ама много го задълбавате, я да си сложи човека switch и case-вете и да не се мъчи 8) .

Автор:  Nikola Kirov [ Съб Мар 26, 2005 2:02 pm ]
Заглавие: 

Аз наскоро писах подобно нещо.Имах над 70 функции. За да се работи прегледно и удобно ги разбих на 4 групи които са с отделен swich-case.
Правих програматор и се налагаше да гоня и бързина така че сравнявах бързодеиствие с друг вариант които бях направил с масив с указатели. Оказа се че резултатите са сходни. Просто варианта с swich-case е по лесен за настроиване и модификация

Автор:  Цецо [ Съб Мар 26, 2005 6:57 pm ]
Заглавие: 

Е не би трябвало резултата по бързодействие да е сходен. SWITCH - CASE минава през всички CASE-ове докато стигне до подходящия. Масива с указатели би трябвало да работи със сумиране+индексиране. Нещо по-важно времето за достъп до функцията при втория метод би трябвало да е константен докато при първия времето расте с номера на функцията. Така си мисля.

Ще експериментирам и двата варианта. Не съм сигурен че компилатора ми поддържа указатели към функция - не че е нещо сложно, ама за MPLAB C18 иде реч.

Автор:  Nikola Kirov [ Съб Мар 26, 2005 9:01 pm ]
Заглавие: 

Идеята с разбиването на групи е че първо проверяваш в кои блок е съответната функция и предаваш управлението в съответния swich-case.
Taka при мен се получи че максималното време за изпълнение беше незначително повече при този метод.Но затова пък по удобно.

Автор:  ToHu [ Съб Мар 26, 2005 9:52 pm ]
Заглавие: 

Хммм не е точно така за switch-а .. компилатора си го оптимизира по самите стойности в switch и ако са типоследователни го изпълнява не с проверка на всички докато открие разлика, ами с указател.

Автор:  Nikola Kirov [ Нед Мар 27, 2005 1:45 am ]
Заглавие: 

Виж за това не знам. Разбиването на групи го правих за мое удобство наи вече. А го правих за PC и не съм следил какво генерира компилатора. Просто засичах времената за изпълнение и се оказа че разликата е съвсем малка.

Автор:  [ Нед Мар 27, 2005 2:14 am ]
Заглавие: 

Масива от указатели има и друго предимство, а то е когато програмата се пише от колектив ( можеш да раздадеш на отделните програмисти кои функции да напишат ) , или пък когато за по лесното поддържане на софтуера е нужно отделните функции да са в различни файлове, по смислово съдържание, а пък и програмата е по прегледна с масива от функции. Бързодействието зависи от компилатора, процесора и дали case-вете са последователни числа ( ако са последователни числа, добрите компилатори реализират таблица с jmp към съответния case ). Най-добре е да се направи тестова програма, реализираща и двата варианта и да се види какво генерира компилатора. Обаче използваш ли масив, прави проверка за обхвата на индекса, защото никой C/C++ компилатор не се интересува дали извикваната функция има нулев(NULL) или различен от нула адрес ( в някои процесори на нулев адрес има системни подпрограми ) MCC18 компилатора поддържа указатели към функции.

Страница 1 от 2 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/