为什么要写这篇
本来以为我对函数指针掌握的可以了,可以用于回调函数,可以用来做跳转表。直到前几天看到一篇文章嵌入式中的合作开发–函数指针 刷新了我的认知,知道自己认识得还不够,这么实用的一个功能竟然没有发掘到。
在嵌入式软件开发中,一个项目往往需要多人协作完成。现在完成程序移植工作时深有体会,这时候想到的是公司有个规范就好了,都按照这个规范来就可以减少不少费时费力的工作,提高些工作效率。前些天我记得看见嵌入式大杂烩分享了一篇,该拜读拜读,但是小公司形不成规范也不是啥难理解的事,因为工程师之间的协作并不频繁。公司没有要求,但自己有体会,有想让自己脱离这种现状的迫切。
A完成项目的整体逻辑功能,A不能面面俱到,因此一些函数的具体实现交给了B来完成,B要完成的函数很多,A就不大可能一个个名字想好之后再交给B,这就导致B提高的函数名的命名规则不符合A的习惯,A用起来很不爽。A可以自己再声明一个自己喜欢的函数名,并通过函数指针来获得B的功能。
前些天有个同事要针对一款蓝牙芯片写一个公版程序,他把底层接口函数和QC2.0协议 源文件交给我来实现,实现后函数的命名方式和他的公版程序有点儿格格不入,于是应他的要求有把命名都改成了他要求的那种,而我依据原来命名方式调用的文件呀,统统都替换掉了,那叫个费事呀。
了解到通过函数指针可以实现这种方式后,就想着怎么用上。
定义函数的文件
#include "definefunc.h"
/**
* set_gpio_init_input
* @brief set gpio init to input
* @param gpio_pin 参数描述: gpio pin
*/
static void
set_gpio_init_input(Gpio_Pin gpio_pin)
{
printf("from function : %s \n", __FUNCTION__);
}
/**
* set_gpio_init_output
* @brief set gvset gpio init to output
* @param gpio_pin 参数描述: gpio pin
*/
static void
set_gpio_init_output(Gpio_Pin gpio_pin)
{
printf("from function : %s \n", __FUNCTION__);
}
/**
* AQ_Gpio_Mode_Set
* @brief set gvset gpio init to input/output
* @param gpio_pin 参数描述: gpio pin
* @param direction 参数描述: input / output
*/
void
AQ_Gpio_Mode_Set(Gpio_Pin gpio_pin, Gpio_Direction direction)
{
if(direction == GPIO_INPUT)
{
set_gpio_init_input(gpio_pin);
}else if(direction == GPIO_OUTPUT)
{
set_gpio_init_output(gpio_pin);
}else
{
/* no code */
}
}
/**
* AQ_Get_Gpio_Input_Data
* @brief get gpio input level
* @param gpio_pin 参数描述: gpio pi
* @retval Gpio_Level 返回值描述: high / low
*/
Gpio_Level
AQ_Get_Gpio_Input_Data(Gpio_Pin gpio_pin)
{
printf("from function : %s \n", __FUNCTION__);
}
/**
* AQ_Gpio_Output_Data
* @brief set gpio output level
* @param gpio_pin 参数描述: gpio pin
* @param gpio_data 参数描述: high / low
*/
void
AQ_Gpio_Output_Data(Gpio_Pin gpio_pin, Gpio_Level gpio_data)
{
printf("from function : %s \n", __FUNCTION__);
}
/**
* AQ_Gpio_Output_Data_toggle
* @brief set gpio output level toggle
* @param gpio_pin 参数描述: gpio pin
*/
void
AQ_Gpio_Output_Data_toggle(Gpio_Pin gpio_pin)
{
printf("from function : %s \n", __FUNCTION__);
}
/**
* AQ_Get_Adc_Data_mV
* @brief get adc filter mV value
* @param channel 参数描述: adc channel
* @retval Gpio_Data 返回值描述: gpio data ,Range : 0 ~ 5500mV
*/
Gpio_Data
AQ_Get_Adc_Data_mV(Adc_Channel channel)
{
printf("from function : %s \n", __FUNCTION__);
}
定义函数的头文件
#ifndef __DEFINEFUNC_H__
#define __DEFINEFUNC_H__
#include <stdio.h>
#include <stdint.h>
typedef uint8_t Gpio_Pin;
typedef uint32_t Adc_Channel;
typedef uint32_t Gpio_Data;
typedef enum direction
{
GPIO_INPUT = 1,
GPIO_OUTPUT,
} Gpio_Direction;
typedef enum level
{
GPIO_DATA_LOW_LEVEL = 0,
GPIO_DATA_HIGH_LEVEL = 1,
}Gpio_Level;
void AQ_Gpio_Mode_Set(Gpio_Pin gpio_pin, Gpio_Direction direction);
Gpio_Level AQ_Get_Gpio_Input_Data(Gpio_Pin gpio_pin);
void AQ_Gpio_Output_Data(Gpio_Pin gpio_pin, Gpio_Level gpio_data);
void AQ_Gpio_Output_Data_toggle(Gpio_Pin gpio_pin);
Gpio_Data AQ_Get_Adc_Data_mV(Adc_Channel channel);
#endif
重定义函数名的源文件
#include "redefinefunc.h"
Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = NULL;
void (*set_gpio_output_data)(Gpio_Pin gpio_pin, Gpio_Level gpio_data) = NULL;
void (*set_gpio_output_toggle)(Gpio_Pin gpio_pin) = NULL;
Gpio_Data (*get_adc_filter_mV_val)(Adc_Channel channel) = NULL;
int init_func_get_gpio_input_data(Gpio_Data(*func_handle)(Gpio_Pin));
int init_func_set_gpio_output_data(void(*func_handle)(Gpio_Pin, Gpio_Data));
int init_func_set_gpio_output_toggle(void(*func_handle)(Gpio_Pin));
int init_func_get_adc_filter_mV_val(Gpio_Data(*func_handle)(Adc_Channel));
/**
* set_gpio_init_input
* @brief set gpio init to input
* @param gpio_pin 参数描述: gpio pin
*/
void
set_gpio_init_input(Gpio_Pin gpio_pin)
{
AQ_Gpio_Mode_Set(gpio_pin, GPIO_INPUT);
}
/**
* set_gpio_init_output
* @brief set gvset gpio init to output
* @param gpio_pin 参数描述: gpio pin
*/
void
set_gpio_init_output(Gpio_Pin gpio_pin)
{
AQ_Gpio_Mode_Set(gpio_pin, GPIO_OUTPUT);
}
/**
* init_func_get_gpio_input_data
* @brief initialize the function : get_gpio_input_data
* @param func_handle 参数描述: function pointer
* @retval int 返回值描述: OK
*/
int init_func_get_gpio_input_data(Gpio_Data(*func_handle)(Gpio_Pin))
{
get_gpio_input_data = func_handle;
return 1;
}
/**
* init_func_set_gpio_output_data
* @brief initialize the function : set_gpio_output_data
* @param func_handle 参数描述: function pointer
* @retval int 返回值描述: OK
*/
int init_func_set_gpio_output_data(void(*func_handle)(Gpio_Pin, Gpio_Data))
{
set_gpio_output_data = func_handle;
return 1;
}
/**
* init_func_set_gpio_output_toggle
* @brief initialize the function : set_gpio_output_toggle
* @param func_handle 参数描述: function pointer
* @retval int 返回值描述: OK
*/
int init_func_set_gpio_output_toggle(void(*func_handle)(Gpio_Pin))
{
set_gpio_output_toggle = func_handle;
return 1;
}
/**
* init_func_get_adc_filter_mV_val
* @brief initialize the function : get_adc_filter_mV_val
* @param func_handle 参数描述: function pointer
* @retval int 返回值描述: OK
*/
int init_func_get_adc_filter_mV_val(Gpio_Data(*func_handle)(Adc_Channel))
{
get_adc_filter_mV_val = func_handle;
return 1;
}
/**
* init_interface_function
* @brief initialize the interface function
*/
void init_interface_function(void)
{
init_func_get_gpio_input_data(AQ_Get_Gpio_Input_Data);
init_func_set_gpio_output_data(AQ_Gpio_Output_Data);
init_func_set_gpio_output_toggle(AQ_Gpio_Output_Data_toggle);
init_func_get_adc_filter_mV_val(AQ_Get_Adc_Data_mV);
}
重定义函数名的头文件
#ifndef __REDEFINEFUNC_H__
#define __REDEFINEFUNC_H__
#include "definefunc.h"
void set_gpio_init_input(Gpio_Pin gpio_pin);
void set_gpio_init_output(Gpio_Pin gpio_pin);
void init_interface_function(void);
extern Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin);
extern void (*set_gpio_output_data)(Gpio_Pin gpio_pin, Gpio_Level gpio_data);
extern void (*set_gpio_output_toggle)(Gpio_Pin gpio_pin);
extern Gpio_Data (*get_adc_filter_mV_val)(Adc_Channel channel);
#endif
测试函数调用
#include "redefinefunc.h"
int main(void)
{
init_interface_function();
set_gpio_init_input(1);
set_gpio_init_output(1);
get_gpio_input_data(1);
set_gpio_output_data(1, 1);
set_gpio_output_toggle(1);
get_adc_filter_mV_val(1);
return 0;
}
运行结果
代码优化
现在这样虽然达到了目的,但改动起来还是太大了,虽然没有函数重写,但也只比重写省事了一丢丢。总的来说就是重新定义相关函数类型的函数指针变量,然后通过函数给其赋值,然后将函数指针变量声明为外部调用,若函数赋值在函数调用之后,那么直接导致程序跑飞,要时常注意这问题那真谈不上多好用。
更好的方法是函数指针变量直接在定义时赋值, 而不是NULL,然后直接声明为外部调用。
.c文件
Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = AQ_Get_Gpio_Input_Data;
.h文件
extern Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin);
你认为这已经是最好的方法了吗?实际上还可以化简。将声明,定义和初始化 合而为一,去掉源文件,直接写在头文件里。因为在头文件里定义变量会导致被引用时,同一变量被多次分配内存,因此要将其前面加static声明为静态的。
.h文件
static Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = AQ_Get_Gpio_Input_Data;
最近了解到一种更简单的方法:通过宏定义
#define get_gpio_input_data AQ_Get_Gpio_Input_Data
测试例子如下
#include <stdio.h>
#include <stdint.h>
void test(void)
{
printf("into function: %s , file : %s, line : %d \n", __FUNCTION__, __FILE__, __LINE__);
}
void (*test_a)(void) = test;
#define test_b test
int main(void)
{
test();
test_a();
test_b();
return 0;
}
输出结果为:
into function: test , file : test1.c, line : 6
into function: test , file : test1.c, line : 6
into function: test , file : test1.c, line : 6