# c language: function pointer, callback and event-driven ###### tags: `linux` `C LANGUAGE` `callback` `event driven` Authors: WhoAmI, CrazyMonkey email: kccddb@gmail.com Date: 20230416 Copyright: CC BY-NC-SA <style> .blue { color: blue; } .bgblue { color: blue; font-size: 24px; font-weight: bold; } .red { color: red; font-size: 24px; font-weight: bold; } .bgred { color: red; font-size: 28px; font-weight: bold; } h1 {text-align: center;} </style> ![](https://i.imgur.com/6VACvIK.jpg) ![](https://i.imgur.com/LYuGlu8.jpg) HW: 如果 每個 action 有 多種狀態 (輸入 event sequence e0 e1 ... e2) 例如 stat 0, stat 1, stat 2 您會如何設計? 例如 e0 e1 e2 則正確, 否則 錯誤 (例如 e0 e0 e1) 錯誤 Time Dependent FSM(Fine State Machine) 問題 ![](https://i.imgur.com/s1cBJyS.jpg) **function pointer 是c 語言的重要技巧, 一般初學者比較少接觸, 然而在很多實務設計, 例如 AJAX(Asynchronous JavaScript and XML), FSM(Fine State Machine)... 中會用到, 此外 c 語言運用物件導向觀念亦會使用到.** **處理 time-dependent FSM 也是 網路程式的重要問題** 例如 pop3 SSL 網路驗證 :::info Linux signal 就是此種 function pointer typedef void (*sighandler_t)(int); sighandler_t 是一新 type sighandler_t signal(int signum, sighandler_t handler); 此種方式可以讓設計者自行設計 handler ::: 另一例子 qsort void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); :::success Event loop (JavaScript execution flow, Win32 proramming) 是類式的觀念, 也都可能用到callback 與 OOP(Object-oriented programming) 的方法. ::: ```c= //callfunc.c /* *Copyright (C) GPL *Version: 1.0 2018/09/18 *Authors: WhoAmI * *DEMO: * gcc -Wall callfunc.c -o callfunc * Enable stdout * gcc -Wall -DTEST callfunc.c -o callfunc * */ #include <stdio.h> #include <stdlib.h> #include <string.h> typedef int (*myFunc)(int, int); //myFunc is a new type static int modulus(int a,int b); static int add(int a,int b); static int myCallback(myFunc *pthis,int a,int b); static int myCallbacks(myFunc *pthis,int a,int b); #ifdef TEST #define debugx(x) x #else #define debugx(x) #endif static int add(int a,int b) { return a+b; } static int modulus(int a,int b) { return a%b; } static int myCallback(myFunc *pthis,int a,int b) { return (*pthis)(a,b); } //這也是一種設計技巧 static myFunc my_func[]={add,modulus,NULL}; //functions in array static int myCallbacks(myFunc *pthis,int a,int b) { int i; myFunc *p; for(i=0,p= pthis;*p!=NULL;i++) { debugx( printf("[%d] return=%d\n",i,(*p)(a,b))); p++; } return 0; } int main (int argc,char *argv[]) { myFunc myAdd=&add; printf("function pointer: myAdd(8,3)=%d\n",(*myAdd)(8,3)); printf("callback: %d\n",myCallback((myFunc *) &myAdd,8,3)); myCallbacks(my_func,8,3); exit(EXIT_SUCCESS); } ``` 另一例子讓讀者熟悉 callback, event listener, ...這些特別的寫程式方法 在此例子中只要增加 Event 中的元素即可執行依照 name 執行 action! 例如 一位設計團隊一員設計一action, 最後再合起來, 如此更能容易除錯與設計 ```c= //hander.c /* *Copyright (C) GPL *Version: 1.0 2018/09/18 *Authors: WhoAmI(cddb.tw@gmail.com) *DEMO:callback, event, listener * * gcc -Wall hander.c -o hander * */ #include <stdio.h> #include <stdlib.h> #include <string.h> typedef int (*myFunc)(int, int); //myFunc is a new type static int modulus(int a,int b); static int add(int a,int b); typedef struct levent { char *name; myFunc *action; }Event; static Event eventlistener[] = { {"add", add}, {"modulus",modulus}, {NULL,NULL} }; static int add(int a,int b) { return a+b; } static int modulus(int a,int b) { return a%b; } static int CallEvent(char *name, Event *event) { char *p; Event *e; myFunc pthis; int a,b; a=5;b=2; for(e=event; e->name!=NULL; e++){ if(strcmp(e->name,name)==0){ pthis=e->action; printf("Find and do %s(%d,%d):%d\n",e->name,a,b,(*pthis)(a,b)); } } return 0; } int main (int argc,char *argv[]) { CallEvent("add",eventlistener); CallEvent("modulus",eventlistener); exit(EXIT_SUCCESS); } ``` ![](https://i.imgur.com/rJubQz6.jpg) <h1> Event-Driven 的設計方法 </h1> :::success **運用function pointer 當 action, Event-Driven 的設計方法** ::: :::info 在此例子中只要增加 Event 中的元素即可執行依照 name 執行 action! 例如 一位設計團隊一員設計一action, 最後再合起來, 如此更能容易除錯與設計 **variable argument lists+function pointer+variadic function 可以彈性設計 action** 您也可 定義 action 的 return value 方便除錯 ::: ```c= /* demo WhoAmI 2022 */ #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> typedef int (*pAction)(void *fmt,...); //pAction is a new type typedef struct levent { char *name; pAction *action; }Event; static int prnstr(void *str,...) { return printf("%s\n",str); } static int prnid(void *str,...) { char *p; int i; va_list arguments; i=0; va_start ( arguments, str ); p=va_arg ( arguments, void * ); for( ;p!=NULL; ){ if(p){ printf("string <%s>:",p); p=va_arg ( arguments, void * ); i++; } else break; } va_end ( arguments ); return i; } static Event eventlistener[] = { {"print", prnstr}, {"pid", prnid}, {NULL,NULL} }; static int CallEvent(char *name, Event *event) { Event *e; pAction pthis; char *p="ha ha ha!"; char *p2="ha ha ha!!"; for(e=event; e->name!=NULL; e++){ if(strcmp(e->name,name)==0){ pthis=e->action; printf("Find and do '%s':%d\n",e->name,(*pthis)(e->name,p,p2,NULL)); } } return 0; } int main (int argc,char *argv[]) { CallEvent("print",eventlistener); printf("\n\n"); CallEvent("pid",eventlistener); exit(EXIT_SUCCESS); } ``` Homework: 思考此方法的優點 Appendix: [State Machine Design in C, by David Lafreniere](https://www.codeproject.com/Articles/1275479/State-Machine-Design-in-C) --- 善用 Macros ```c= //gcc -Wall demodef.c -o demodef #define VAL(x) val##x #define STR(x) #x //do{ ...}while(0); #define prn(x) do{\ printf("%s val=%d\n",STR(x),60);\ printf("Hello define! line=%d\n",__LINE__);\ }while(0) //Variadic Macros #define QUOTE(...) #__VA_ARGS__ #define eprintf(...) fprintf (stderr, __VA_ARGS__) //multiple lines const char *name="OK=Line 1 OK \ Line 2\ Line 3\ "; const char *fname = QUOTE(OK= abc " 123 " " 456 " def); int main() { int val99=10; int val911=911; prn(Hello C); printf("Hello %d %d\n", VAL(99),VAL(911)); //val99 val911 eprintf("name=%s\n fname=%s\n",name,fname); eprintf("Output to stderr date: %s line: %d\n",__DATE__,__LINE__); return 0; } ``` **typeof** ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> // https://gcc.gnu.org/onlinedocs/gcc/Typeof.html #define max(a,b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a > _b ? _a : _b; }) struct fruit{ int weight; int cost; }; static struct fruit *put_fruit() { struct fruit *_fruit; _fruit = malloc(sizeof(struct fruit)); if(_fruit == NULL) { printf("malloc error.\n"); return NULL; } _fruit->weight = 10; _fruit->cost = 2; return _fruit; } int main(int argc, char *argv[]) { typeof(put_fruit()) _fruit; //不會執行 put_info(), 取得型態 struct fruit * _fruit = put_fruit(10,20); if(_fruit){ printf("weight:%d\n", _fruit->weight); printf("cost:%d\n", _fruit->cost); printf("max(2,3)=%d\n", max(2,3)); free(_fruit); } return 0; } ```