在純 C 環境(RT-Thread + Finsh Shell)中實現類 C# 的 event/delegate 機制,支援:多播委託、動態增減訂閱、型別安全的回調調用。以「老板通知同事關閉股票/ NBA」為例示。
typedef struct _List {
void** items; int count;
void (*Add)(struct _List*, void*);
void (*Remove)(struct _List*, void*);
void (*Delete)(void*);
} List;
Add:重新分配 count+1 空間,去重後追加;Remove:分配 count-1 空間,過濾目標指標;Delete:釋放 items 再釋放自身。typedef void (*Event)(void*); // 回調簽名:單一 void* 參數
typedef struct _Entrust {
List* funcs;
void (*Add)(struct _Entrust*, void* callback);
void (*Remove)(struct _Entrust*, void* callback);
void (*Update)(struct _Entrust*); // 遍歷調用所有回調
void (*Delete)(void*);
} Entrust;
Update 逐個取出函數指標並調用:(*(Event*)funcPtr)(funcPtr),利用「函數指標即物件」特性,將同一指標同時作為 this 與回調入口。
typedef struct _Boss {
Entrust* evt;
char* action;
char* (*GetState)(void*);
void (*SetState)(void*, char*);
void (*Notify)(void*); // 呼叫 evt->Update()
void (*Delete)(void*);
} Boss;
typedef struct _StockObserver {
char* name; Boss* sub;
void (*CloseStock)(void*); // 回調函數
void (*Delete)(void*);
} StockObserver;
// 關鍵技巧:從回調指標反推物件指標
#define ClassEntry(ptr, type, member) \
((type*)((char*)(ptr) - (unsigned long)&(((type*)0)->member)))
static void CloseStock(void* p) {
auto obs = ClassEntry(p, StockObserver, CloseStock);
rt_kprintf(" %s,%s關閉股票行情,繼續工作\n",
obs->sub->GetState(obs->sub), obs->name);
}
void EntrustObs(void) {
Boss* huhasan = BossCreate(sizeof(Boss));
StockObserver* ws1 = StockObserverCreate("魏關姹", huhasan, sizeof(StockObserver));
NBAObserver* ws2 = NBAObserverCreate("易管查", huhasan, sizeof(NBAObserver));
huhasan->evt->Add(huhasan->evt, &(ws1->CloseStock));
huhasan->evt->Add(huhasan->evt, &(ws2->CloseNBA));
huhasan->SetState(huhasan, "我胡漢三回來啦");
huhasan->Notify(huhasan); // 觸發兩個回調
huhasan->Delete(huhasan);
ws1->Delete(ws1); ws2->Delete(ws2);
}
FINSH_FUNCTION_EXPORT(EntrustObs, Entrust Observer Modle);
rt_malloc/rt_free,符合嵌入式環境;FINSH_FUNCTION_EXPORT 讓測試函數直接在命令列呼叫。可進一步封裝為泛型 Event<T>(以巨集生成特化版本)、加入線程安全鎖、支援優先級調用、異步佇列等。