Решил поиграться с указателями на функцию (callback, функции обратного вызова) и массивом указателей на функцию.
Что это?
Функция располагается в памяти по определенному адресу, который можно присвоить указателю в качестве его значения. Адресом функции является ее точка входа. Именно этот адрес используется при вызове функции. Так как указатель хранит адрес функции, то она может быть вызвана с помощью этого указателя. Он позволяет также передавать ее другим функциям в качестве аргумента. В программе на С адресом функции служит ее имя без скобок и аргументов (это похоже на адрес массива, который равен имени массива без индексов).
Небольшой ликбез.
Объявление указателя на функцию:
int (*pt2Function)(float, char, char) = NULL; // C
int (TMyClass::*pt2Member)(float, char, char) = NULL; // C++
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL; // C++
int (TMyClass::*pt2Member)(float, char, char) = NULL; // C++
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL; // C++
Присвоение указателю на функцию адреса функции: это просто. Просто берете имя уже объявленной и определенной функции и перед ним добавляете операцию взятия адреса &. Хотя на многих компиляторах можно ее опустить, но для написания переносимого когда использование & обязательно!!!
// C
int DoIt (float a, char b, char c){ printf("DoIt\n"); return a+b+c; }
int DoIt (float a, char b, char c){ printf("DoIt\n"); return a+b+c; }
int DoMore(float a, char b, char c)const{ printf("DoMore\n"); return a-b+c; }
pt2Function = DoIt; // short form
pt2Function = &DoMore; // correct assignment using address operator
// C++
class TMyClass
{public:
int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << "TMyClass::DoMore" << endl; return a-b+c; };
/* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoMore; // correct assignment using address operator
pt2Member = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore
pt2Function = DoIt; // short form
pt2Function = &DoMore; // correct assignment using address operator
// C++
class TMyClass
{public:
int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << "TMyClass::DoMore" << endl; return a-b+c; };
/* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoMore; // correct assignment using address operator
pt2Member = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore
Конец.
Идея в том, чтобы создать массив указателей на функцию. В определенном порядке инициализировать каждый элемент массива адресом определенной функции, а затем строить поведение программы, играясь индексом этого массива. Это идея - предтеча другой идеи, о которой я пока говорить не хочу...
Итак, сделаем зарядку. Обычно при зарядке ведут счет: Раз, Два, Три, Четыре, Три, Четыре, Раз, Два... Надеюсь, текст программы будет понятен.
#include <stdio.h>
/* type definition: 'pfunction' now can be used as type */
typedef void (*pFunction) (void);void one (void)
{
printf("%s(): ONE\n",__func__);
}
void two (void)
{
printf("%s(): TWO\n",__func__);
}
void three (void)
{
printf("%s(): THREE\n",__func__);
}
void four (void)
{
printf("%s(): FOUR\n",__func__);
}
int main(void) {
printf("%s \n", __func__);
int counter;
/* define arrays and initialize each element to NULL */
pFunction funcArray[8] = {NULL};
/* assign the function's address */
funcArray[0] = funcArray[6] = &one; /* correct assignment using address operator */
funcArray[1] = funcArray[7] = &two;
funcArray[2] = funcArray[4] = &three;
funcArray[3] = funcArray[5] = &four;
/* calling a functions using an index to address the function pointers */
for (counter = 0; counter < 8; ++counter) {
funcArray[counter]();
}
return 0;
}
/* type definition: 'pfunction' now can be used as type */
typedef void (*pFunction) (void);void one (void)
{
printf("%s(): ONE\n",__func__);
}
void two (void)
{
printf("%s(): TWO\n",__func__);
}
void three (void)
{
printf("%s(): THREE\n",__func__);
}
void four (void)
{
printf("%s(): FOUR\n",__func__);
}
int main(void) {
printf("%s \n", __func__);
int counter;
/* define arrays and initialize each element to NULL */
pFunction funcArray[8] = {NULL};
/* assign the function's address */
funcArray[0] = funcArray[6] = &one; /* correct assignment using address operator */
funcArray[1] = funcArray[7] = &two;
funcArray[2] = funcArray[4] = &three;
funcArray[3] = funcArray[5] = &four;
/* calling a functions using an index to address the function pointers */
for (counter = 0; counter < 8; ++counter) {
funcArray[counter]();
}
return 0;
}
В программе я просто в цикле for() организую последовательный вызов callback’ов, изменяя индекс массива указателей. Однако - идея №2 - индекс может изменяться под воздействием, например, volatile переменной, которая, к примеру, меняется в прерывании, или же переменная - это значение I/O порта, настроенного на вход, и в соответствии с этим изменяется поведение основной нити программы. Но это только идея, а так как я особо не видел применение Идеи №2 на практике, то это просто идея побаловаться.
Результат работы:
main
one(): ONE
two(): TWO
three(): THREE
four(): FOUR
three(): THREE
four(): FOUR
one(): ONE
two(): TWO
Комментариев нет:
Отправить комментарий