Try   HackMD

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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) 問題

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

function pointer 是c 語言的重要技巧, 一般初學者比較少接觸, 然而在很多實務設計, 例如 AJAX(Asynchronous JavaScript and XML), FSM(Fine State Machine) 中會用到, 此外 c 語言運用物件導向觀念亦會使用到.

處理 time-dependent FSM 也是 網路程式的重要問題
例如 pop3 SSL 網路驗證

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 *));

Event loop (JavaScript execution flow, Win32 proramming) 是類式的觀念, 也都可能用到callback 與 OOP(Object-oriented programming) 的方法.

//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, 最後再合起來, 如此更能容易除錯與設計

//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); }

Event-Driven 的設計方法

:::

在此例子中只要增加 Event 中的元素即可執行依照 name 執行 action!

例如 一位設計團隊一員設計一action, 最後再合起來, 如此更能容易除錯與設計
variable argument lists+function pointer+variadic function 可以彈性設計 action

您也可 定義 action 的 return value 方便除錯

/* 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


善用 Macros

//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

#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; }