😊

C言語でオブジェクト指向(継承)

に公開

概要

C言語を勉強している中でC言語で継承を書けることを初めて知りましたので記事にしました。

継承とは

継承に関する詳しい内容は今回は触れませんが、過去に使用したクラスを流用して新しいクラスを作成するという認識で私はいます。
それをC言語でどうやって実装していくかを簡単に紹介します。

サンプルコード

以下のリンクに今回のコードがあります。
通信を例でやってみました。
https://github.com/begengineer/modanC/blob/main/inheritance/inheritanceC.c

コード説明

  1. ベースラインの定義
    ここで共通化するベースラインの関数を定義します。
    通信だと初期化、送信、受信でしょうか。
// ベースのインターフェース
typedef struct 
{
    int (*init)(void *self);
    int (*send)(void *self,const uint8_t *buf,int len);
    int (*recv)(void *self,uint8_t *buf,int len);
}ComIF;
  1. ドライバ定義
    先ほどのComIFを先頭としたドライバを定義します。
    ここで肝なのが、共通化させるインターフェースは構造体の先頭に持ってきてください。
    それ以外は通信方式に合わせてメンバを変更することも可能です。
// UARTドライバ
typedef struct{
    ComIF base;
    int uart_ch;
}UART_Com;
  1. メゾットの定義
    ここは割愛します。それぞれの関数の内容を記載します。

  2. 初期化
    3で定義したメゾットをそれぞれセットします。

void UART_Com_init(UART_Com *obj,int ch){
    obj->uart_ch = ch;

    // 関数設置
    obj->base.init = UART_init;
    obj->base.send = UART_send;
    obj->base.recv = UART_recv;
}
  1. 使用方法
    基本的にはユーザー側はComIFだけを意識すれば使用できます。(使用例はSPIですが、これまで設定していたUARTでも可能です)
int main(){
    // 宣言
    ComIF *com;
    UART_Com uart;
    SPI_Com spi;

    // 初期化
    SPI_Com_init(&spi,1,8);
    com = (ComIF*)&spi;
    
    // テスト用変数
    const uint8_t * data = "Hello";
    int len = 5;
    int msg = 9;
    uint8_t rbuf[10] = {0};

    // 実際の処理
    com->init(com);
    com->send(com,data,len);
    com->recv(com,rbuf,sizeof(rbuf));
}

最後に

このようにC言語で継承をすることで使用者は通信方式を意識することなくデータのやり取りができます。
これでCANでもUARTでもSPIでもメインのコードはあまり変更せずに処理ができるようになりました。

Discussion