構造体の不完全型宣言でメンバを隠蔽する

C言語で、あるモジュールを制御するような構造体があるとする。
どの制御を行うかの選択は上位モジュールに行わせたいが、制御で使用する構造体のメンバは見せたくない、といった場合、構造体の不完全型宣言で解決できる。


以下に等比数列・等差数列を計算するプログラムで、構造体の不完全型宣言を用いた例を書いてみた。

module.h

#ifndef _MODULE_H_INCLUDED_
#define _MODULE_H_INCLUDED_
#include <stdio.h>

enum e_type {
    TYPE_EQUAL_RATIO,	/* 等比数列 */
    TYPE_EQUAL_DIFF  	/* 等差数列 */
};

/*
 * 不完全型宣言
 * 構造体のポインタのみ公開する。実体を定義することはできない。
 */
typedef struct tag_state *State;

State get_state(int val, int k, enum e_type type);
void exec_state(State state);
#endif

module.c

#include "module.h"

/* 等比数列・等差数列の構造体 */
static void func_mul(State);
static void func_add(State);

/* 状態管理構造体 */
struct tag_state {
    int index;
    int val;
    int k;
    void (* pfunc_calc)(State);
};

/* 等比数列・等差数列の状態管理構造体の実体を定義 */
static struct tag_state equal_ratio_state = { 0, 0, 0, func_mul };
static struct tag_state equal_diff_state = { 0, 0, 0, func_add };


/* 状態管理構造体のハンドルを取得する */
State get_state(int val, int k, enum e_type type)
{
    State state;

    switch (type) {
        case TYPE_EQUAL_RATIO:
            state = &equal_ratio_state;
            break;
        case TYPE_EQUAL_DIFF:
            state = &equal_diff_state;
            break;
        default:
            break;
    }

    state->val = val;
    state->k   = k;

    return state;
}

/* 処理実行 */
void exec_state(State state)
{
    state->pfunc_calc(state);
}

/* 等比数列処理 */
static void func_mul(State state)
{
    int result = state->val * state->k;
    printf("[%d] MUL: %d * %d = %d\n", state->index, state->val, state->k, result);

    state->index++;
    state->val = result;
}

/* 等差数列処理 */
static void func_add(State state)
{
    int result = state->val + state->k;
    printf("[%d] ADD: %d + %d = %d\n", state->index, state->val, state->k, result);

    state->index++;
    state->val = result;
}

main.c

#include <stdio.h>
#include "module.h"

int main()
{
    /* 等比数列・等差数列それぞれのハンドルを取得する */
    State eq_ratio = get_state(1, 2, TYPE_EQUAL_RATIO);
    State eq_diff = get_state(1, 3, TYPE_EQUAL_DIFF);
    int i;

    /* 処理実行 */
    for (i=0; i<4; i++) {
        exec_state(eq_ratio);
        exec_state(eq_diff);
    }

    return 0;
}


実行結果

$ ./a.out                     
[0] MUL: 1 * 2 = 2
[0] ADD: 1 + 3 = 4
[1] MUL: 2 * 2 = 4
[1] ADD: 4 + 3 = 7
[2] MUL: 4 * 2 = 8
[2] ADD: 7 + 3 = 10
[3] MUL: 8 * 2 = 16
[3] ADD: 10 + 3 = 13


あまりメリットを感じられる例じゃないかも…



参考ページ
http://ch.cri-mw.co.jp/hirase/707.html
K.Maebashi's home page:C言語ヨタ話:その3 「モジュールと命名とヘッダファイルと」