c語言實現this指標效果

pie_thn發表於2023-12-04

概要

由於目前在做一個比較複雜的嵌入式專案,想要藉此提升一下程式碼的結構設計能力,所以想要以物件導向的思想來完成這個專案,即把每個板載外設資源視為一個物件,採用msp+bsp的模式,對每個bsp外設實現物件化處理,現有方案需要手動傳入物件引用,呼叫方法時比較麻煩,所以考慮簡化呼叫方式。

物件導向實現思路

現有方案

物件就是具有屬性與方法的集合體,以LED舉例,它的屬性就是引腳亮使能標誌等,方法就是
瞭解到現有的c語言物件導向實現方法都需要手動傳入物件的引用,如下這種方式:

typedef struct _LED_TYPEDEF{
	//屬性
	struct _Privated_Attr{
		GPIO_InitTypeDef GPIO_Body;
		GPIO_TypeDef *GPIOx;
		GPIO_PinState ENbit;
	} Privated_Attr; 
	
	//方法
	void (*LightUp)(struct _LED_TYPEDEF *);
	void (*LightOff)(struct _LED_TYPEDEF *);
}LED_TypeDef;

上面的結構體中有一個嵌入的結構體變數,主要用途就是類似私有變數,類外不可直接訪問的目的,也是出於屬性只由方法操作,這樣可以對屬性值的合理性做出一定限制與約束,然後方法的引數必須加入物件的引用,也就是傳入物件地址。
此時呼叫方法為:

//例項化物件
LED_TypeDef BSP_LED1;
LED_TypeDef BSP_LED2;

//物件方法使用
BSP_LED1.LightUp(&BSP_LED1); //led1亮
BSP_LED2.LightOff(&BSP_LED2);//led2滅

這種方式比較麻煩,所以有必要引入c++this指標方式。

this方案

目的:為了簡寫物件方法的呼叫模式。
所謂的this指標可以簡單理解為編譯器幫我們把物件引用傳遞到方法中了。

單一bsp方案

此單一bsp意為板子上只有這一個外設,所以這個bsp物件只需要一個this指標,以uart舉例

typedef struct _UART_OBJ_TYPEDEF{
	//屬性
	struct _PrivateAttr{
		uint16_t Buf_Cnt;
		UART_HandleTypeDef UARTxHandler;		
	}PrivateAttr;
	uint8_t Is_RX_OV;
	uint8_t Is_RX_OK;
	uint8_t RX_Buf[UART_RX_MAX_SIZE];
	
	//方法
	void (*SendChar)(struct _UART_OBJ_TYPEDEF *,uint8_t chr);
	void (*SendStr)(struct _UART_OBJ_TYPEDEF *,uint8_t *str);
	void (*ClearBuf)(void);
	void (*ClearFlag)(void);
	void (*BufAppend)(uint8_t byte);
	uint16_t (*GetBufLength)(void);
}UART_Obj_TypeDef;

可以看到方法中不再需要手動傳入物件引用了。

UART_Obj_TypeDef UART_Debug_Obj; //例項化物件
static UART_Obj_TypeDef *mthis = &UART_Debug_Obj; //this指標實現物件引用

這樣就利用static檔案的作用域實現為每個bsp物件實現一個this指標效果。

//將資料放入緩衝區
UART_Debug_Obj.BufAppend(res);
//清空緩衝區
UART_Debug_Obj.ClearBuf();

多個同類bsp方案

//bsp物件的this陣列偏移量
#define BSP_LED1_OFFSET 0
#define BSP_LED2_OFFSET 1

//間接改變this的指向
#define BSP_LED1	(this_ledx = BSP_LED1_OFFSET);_BSP_LED1
#define BSP_LED2	(this_ledx = BSP_LED2_OFFSET);_BSP_LED2

struct _LED_TYPEDEF;

typedef struct _LED_TYPEDEF{
	struct _Privated_Attr{
		GPIO_InitTypeDef GPIO_Body;
		GPIO_TypeDef *GPIOx;
		GPIO_PinState ENbit;
	} Privated_Attr;
	void (*LightUp)(void);
	void (*LightOff)(void);
}LED_TypeDef;

使用宏定義的方式間接改變this的指向

//例項化2個同類物件
LED_TypeDef _BSP_LED1;
LED_TypeDef _BSP_LED2;
//this指標與this陣列
static LED_TypeDef* This_Arr[LED_NUM] = {&_BSP_LED1,&_BSP_LED2};
static LED_TypeDef* mthis;
//this指向偏移量(因為外面要用,所以名字不要衝突,最好和bsp物件相關)
uint8_t this_ledx = BSP_LED1_OFFSET;
//方法定義
void LightUp(){
	mthis = This_Arr[this_ledx]; //透過this指標偏移來確定使用哪個物件
	HAL_GPIO_WritePin(mthis->Privated_Attr.GPIOx,\
	mthis->Privated_Attr.GPIO_Body.Pin,\
	mthis->Privated_Attr.ENbit);
}

總結

基於物件導向的思想對於程式碼的結構和可讀性上都有一定的利處,特別在裸機編寫過程中,由於不受系統的干預,對於思路與框架的設計都清晰起來,目前還在不斷改善中,這種方式的缺陷也很明顯,只適用在靜態的情況,要想動態實現只有編譯器能操作了,但還是希望這種方式可以很好的在專案中使用。