[嵌入式学习随笔] 嵌入式开发中的注册机制

本文讨论了在嵌入式开发中,通过列表式、注册式和静态函数注册式实现根据不同事件调用对应处理函数的方法。列表式结构清晰但耦合高,注册式简化了耦合但流程不清晰,而静态函数注册式则实现了高内聚低耦合,适合移植。
摘要由CSDN通过智能技术生成


问题描述

在进行嵌入式开发时,常常会需要根据事件来调用对应的处理函数。
例如发送照片时,可以选择微信发送、小红书发送、qq发送等。
这个流程可以通过很多种方法实现,但它们各自的优缺点是什么呢?


一、列表式

列表式的优点是结构清晰,容易理解;缺点是代码耦合度高,不方便移植和修改。例如以下代码,结构非常清晰,可以看到所有支持的APP种类和相应的处理函数;但是如果在一个新的项目中,提出了新的要求,例如取消对微信的支持,添加对微博的支持,那么需要在send_image这个函数中增加WEIBO_TYPE,如果要不编译微信模块,还需要删除wechat_module.h

// send_iamge.c

#include "wechat_module.h"
#include "red_book_module.h"
#include "qq_module.h"

void send_image(uint8_t app_type, uint8_t *data, uint32_t data_len)
{
	switch (app_type) {
		case WECHAT_TYPE: {
			wechat_image_handler(data, data_len);
			break;
		}
		case RED_BOOK_TYPE: {
			red_book_image_handler(data, data_len);
			break;
		}
		case QQ_TYPE: {
			qq_image_handler(data, data_len);
			break;
		}
		default:
			break;
	}
}

二、注册式

注册式是通过注册的方式将处理函数注册在一个列表中,根据app_type去调用相应的函数。它在一定程度上减少了代码的耦合,但指令处理流程已经没有列表式那么清晰了。而且要先调用wechat_init这个函数才可以,这样虽然减少了增加减少模块时需要修改的代码,但解耦也不是很彻底。

// send_image.h
#define APP_NUM_MAX		20		// 最多支持的APP数量

typedef struct
{
	uint8_t	app_num;
	void (*app_handler[APP_NUM_MAX])(uint8_t *data, uint32_t data_len);
} app_register_list_t;

// send_image.c
static app_register_list_t app_list = {0};

void app_init(void)
{
	wechat_init();
	...
}

void app_register(uint8_t app_type, void (*app_handler)(uint8_t *data, uint32_t data_len))
{
	if (app_type >= APP_NUM_MAX) {
		printf("invalid app type: %d", app_type);
		return;
	}
	app_list.app_handler[app_type] = app_handler;
	app_list.app_num++;
}

void send_image(uint8_t app_type, uint8_t *data, uint32_t data_len)
{
	if (app_type >= APP_NUM_MAX) {
		printf("invalid app type: %d", app_type);
		return;
	}
	if (NULL == app_list.app_handler[app_type]) {
		printf("unregistered app type: %d", app_type);
		return;
	}
	app_list.app_handler[app_type](data, data_len);
}

// wechat.c
static void wechat_image_handler(uint8_t *data, uint32_t data_len)
{
	/* 图片发送 */
}

void wechat_init(void)
{
	app_register(WECHAT_TYPE, wechat_image_handler);
}

三、静态函数注册式

这种方式是在注册式的基础上,使用__attribute__(section)将模块的初始化函数在链接时预先注册到内存中,在程序初始化时再注册需要的模块。这种方法实现了模块和发送动作的完全解耦,在增减模块时只需要修改模块代码,无需修改程序的其它部分。

// send_image.h
#define APP_NUM_MAX		20		// 最多支持的APP数量

extern uint32_t app_type_table_start[];
extern uint32_t app_type_table_end[];

typedef void (*app_init_handler_t)(void);

typedef struct
{
	uint8_t	app_num;
	void (*app_handler[APP_NUM_MAX])(uint8_t *data, uint32_t data_len);
} app_register_list_t;

typedef struct
{
	uint8_t app_type;
	app_init_handler_t init_handler;
} app_init_list_t;

#define ADD_APP_TYPE(app_type, init_handler) \
	const app_init_list_t app_type##_entry __attribute__((used, section(".app_type_init_table"))) = \
	{app_type, init_handler};

// send_image.c
void app_init(void)
{
	app_init_list_t *app_init_ptr = app_type_table_start;
	for (; app_init_ptr < app_type_table_end; ++app_init_ptr) {
		app_init_ptr->init_handler();
	}
}

void app_register(uint8_t app_type, void (*app_handler)(uint8_t *data, uint32_t data_len))
{
	if (app_type >= APP_NUM_MAX) {
		printf("invalid app type: %d", app_type);
		return;
	}
	app_list.app_handler[app_type] = app_handler;
	app_list.app_num++;
}

void send_image(uint8_t app_type, uint8_t *data, uint32_t data_len)
{
	if (app_type >= APP_NUM_MAX) {
		printf("invalid app type: %d", app_type);
		return;
	}
	if (NULL == app_list.app_handler[app_type]) {
		printf("unregistered app type: %d", app_type);
		return;
	}
	app_list.app_handler[app_type](data, data_len);
}

// wechat.c
static void wechat_image_handler(uint8_t *data, uint32_t data_len)
{
	/* 图片发送 */
}

void wechat_init(void)
{
	app_register(WECHAT_TYPE, wechat_image_handler);
}
ADD_APP_TYPE(WECHAT_TYPE, wechat_init);

总结

这三种方法各有利弊,第一种方法结构清晰,适合初学者或者对模块解构要求不高的场景。第二种和第三种结构都不太清晰,但代码非常简洁,耦合度很低。与第二种方法相比,第三种方法需要修改链接脚本,对基本功的要求更高,不适合初学者,但极大地提高了代码的可移植性,实现了模块的高内聚和低耦合。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值