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


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

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

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