C言語でインターフェイスクラスを実現

## Cでのインターフェイスクラス

前回、[リソースの一元管理ライブラリ](https://qiita.com/developer-kikikaikai/items/3e4e950dd1ebd20508d7 "リソースの一元管理ライブラリ")を作成した際に、

- Cでも構造体がカプセル化出来る

と書きました。

その際インスタンスのメソッドが無いから面白味にかけると書いたのですが、
考えてみたら**関数ポインタで普通に実現**出来ますね。
ということでsample作ってみました。


4/29追記 実際どういうときに使うと便利かは、[ここ](https://qiita.com/developer-kikikaikai/items/47b55456ae6e9c287718#c%E8%A8%80%E8%AA%9E%E3%81%A7%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%82%A4%E3%82%B9%E3%82%AF%E3%83%A9%E3%82%B9%E8%A1%A8%E7%8F%BE%E3%81%AE%E6%9C%89%E7%94%A8%E6%80%A7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)に所感を書いてみました。

## サンプル

以下のようにインターフェイスクラスに見立てた構造体を定義しました。
インターフェイスを利用して、実装クラスに各自文化について答えてもらいましょう。

今回はget_any_peopleで実装クラスの情報を取得し、
mainでインターフェイスを利用したインタビューを行うコードにしました。

引数がint *はculture_if を取得するためのID一覧です。

``````````````````C:culture.h

//interface definition
struct culture_if {
        char *(*introduce)();//自己紹介
        char *(*get_name)();//名前
        char *(*answer)(int id);//質問
};

int get_any_people(int *peoples_id);
``````````````````

で、mainはこんな感じ。culture_if を利用して集まった方々に質問します。

``````````````````C:main.c
#include <stdio.h>
#include "culture.h"
#include "flyweight.h"

int main() {
        struct {
                int id;
                char * question;
        } questions[] =
        {
                {FOOD, FOOD_QUESTION},
                {HAPPY, HAPPY_QUESTION},
                {CONPUTER, CONPUTER_QUESTION},
        };

        int max_members[10];
        int i=0;
        int member_num = get_any_people(max_members);

        printf("[インタビューアー] 皆さんこんにちは。本日はよろしくお願いします。まずは軽く自己紹介をお願いします。\n");

        //interface
        struct culture_if *cultureif;

        for( i = 0 ; i < member_num; i ++ ) {
                printf("  [%d番目の方の自己紹介]\n", i+1);
                cultureif = (struct culture_if *) flyweight_get(max_members[i]);
                //自己紹介してもらう
                printf("\t%s\n", cultureif->introduce() );
        }

        printf("\n");
        printf("[インタビューアー] ありがとうございます。それではこれから順次質問をしていきたいと思います。\n気軽にお答えください。\n\n");

        int j=0;
        for ( i = 0 ; i < sizeof(questions)/sizeof(questions[0]); i ++ ) {
                printf("Q %d: %s\n", i+1, questions[i].question);
                for( j = 0 ; j < member_num ; j ++ ) {
                       //名前と、質問の回答をもらう
                        cultureif = (struct culture_if *) flyweight_get(max_members[j]);
                       //名前と、質問の回答をもらう
                        printf("  [A:%sさん]\n", cultureif->get_name());
                        printf("\t%s\n", cultureif->answer(i));
                }
                printf("\n");
        }

        printf("[インタビューアー]以上で質問は終わりです。皆さんありがとうございました！\n");
        flyweight_exit();
        return 0;
}
`````````````````````

flyweight_getは[冒頭のライブラリ](https://qiita.com/developer-kikikaikai/items/3e4e950dd1ebd20508d7 "冒頭のライブラリ")のインスタンスを取得するAPIです。
ID指定でインターフェイス実装クラスの実体を取得してます。
メソッドが使えるとより使いやすくなりますね。

インターフェイス実装クラスでは、構造体の実体を用意して、static関数のポインタを設定すればOKです。

``````````````````C:germany.c

#define GNAME "シュバインシュタイガー"

static char * gm_introduce() {
        return "ドイツ人の"GNAME"です。";
}

static char * gm_name() {
        return GNAME;
}

static char * gm_answer(int id) {
        static char *answer[]={
                "ビールにポテト、旨いソーセージがあれば十分だね。",
                "幸せだよ。自分の能力を発揮して働いてるし、素敵な家族がいる。最高だね",
                "ここまでは平気だね、利用されるエラーケースもテストしたかい？",
        };
        return answer[id];
}

//構造体に関数ポインタをセット
static void gm_constructor(void *src) {
        struct culture_if *culture_if = (struct culture_if *)src;
        culture_if->introduce = gm_introduce;
        culture_if->get_name = gm_name;
        culture_if->answer = gm_answer;
}

int Germany_new() {
        struct flyweight_init_s method={
                gm_constructor,
                NULL,
                NULL
        };

        //この中でstruct culture_ifをmallocして、その後gm_constructorが呼ばれる
        int id = flyweight_register_class(sizeof(struct culture_if), 0, &method);
        return id;
}
````````````````````````

~~ソースは[ここ](https://github.com/developer-kikikaikai/practice_design_pattern/tree/try_flyweight_1st/flyweight/interface_sample)~~
~~動作環境: Ubuntu 18.04 Desktop (Ubuntu 14.04 Desktopでも動作確認済み, 大抵のLinux OSなら動くと思います。)~~

2018/07/30追記 ソース消しちゃいました、すいません。

後は実装クラス内でメンバー変数を使いたいなら、以下のようにdefine切っとくと楽かなと思いました。

````````````````````````C:
#define CULTURE_METHOD \
        char *(*introduce)();\
        char *(*get_name)();\
        char *(*answer)(int id);
````````````````````````

## ちなみに

当たり前ですが、こういうCでのオブジェクト指向実現って、懇切丁寧にまとめられてる方いらっしゃるんですね。
参考にします。

http://www.sage-p.com/process/cool.htm#7