lwevt 核心实现:两次 include 生成事件类型,一次遍历完成广播

声明: lwevt 是一个开源项目,作者为 Tilen MAJERLE。本文是阅读该项目源码和文档后整理的学习笔记,用于理解 X-macro 类型生成与广播分发在嵌入式事件系统里的实现方式。本文作者不是该项目的开发者,未参与该项目的任何代码贡献。 文中所有工程细节均来自对开源代码的分析,不代表本文作者的设计决策。

项目仓库:github.com/MaJerle/lwevt

这篇文章讲 lwevt 源码里的三个实现思想:类型系统交给编译器生成、分发退化成一次数组遍历、状态只有三个静态变量。

核心思想

lwevt 把整个事件系统压到最小:170 B 代码加 48 B RAM。三个落点:

X-macro:两次 include 生成类型系统

X-macro 的核心技巧:宏在 include 之前重新定义,让同一个头文件在两次 include 时展开成不同内容。

lwevt_type.h 先给两个宏提供空默认值,再 include 用户的 lwevt_types.h,最后 undef:

#ifndef LWEVT_TYPE_BASIC
#define LWEVT_TYPE_BASIC(name)
#endif
#ifndef LWEVT_TYPE_EXT
#define LWEVT_TYPE_EXT(name, data)
#endif
#include "lwevt_types.h"   /* 用户的事件声明 */
#undef LWEVT_TYPE_BASIC
#undef LWEVT_TYPE_EXT

lwevt.h 第一次 include 前把宏定义成生成枚举成员,第二次定义成生成 union 成员:

/* 第一次:枚举 */
#define LWEVT_TYPE_BASIC(name)   name,
#define LWEVT_TYPE_EXT(name, data) name,
#include "lwevt/lwevt_type.h"

/* 第二次:union */
#define LWEVT_TYPE_EXT(name, data) data;
#include "lwevt/lwevt_type.h"

用户在 lwevt_types.h 里只写一行声明:

LWEVT_TYPE_EXT(LWEVT_TYPE_TEMP_READY, struct { int16_t temp_x10; } temp)

编译后同时得到枚举成员 LWEVT_TYPE_TEMP_READY 和 union 成员 temp,类型与数据绑定,改一处两端生效。

X-macro 展开过程

广播分发:一次遍历调用全部监听器

lwevt_dispatch_ex 只有两个动作:

  1. evt_handle->type = type
  2. for (idx = 0; idx < evt_fncs_cnt; ++idx) evt_fncs[idx](evt_handle)

没有话题过滤,没有匹配逻辑。每个监听器收到每个事件,自己用 switch 或 if 判断 type。数据零拷贝:事件数据就在句柄的 union 里,监听器直接读写。

广播遍历

注册去重与全局单例

设计取舍与边界