数式の評価を原始的に #9

字句の切り出しで直接getchar()を使用しているため、
標準入力以外からの入力に対応させるのが難しい。
例えば文字配列に格納されているものを入力として使用するとか。
ということで、標準入力とnull文字終端文字配列を入力元として動的に変えられるように。
共通のインタフェイスReaderの初期化だけ入力元に応じて別々に行えば、
後は後始末まで共通の呼び出し方が使える。
とりあえず入力をそのまま標準出力に出力するコード。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define UNUSED_ARGUMENT(x) (void)(x)

typedef enum { ANY_READER, STD_READER, STR_READER } ReaderType;

typedef union tagReader Reader;

typedef struct {
    ReaderType type;
    void (*fin)(Reader *);
    int (*get)(Reader *);
} AnyReader;

typedef struct {
    ReaderType type;
    void (*fin)(Reader *);
    int (*get)(Reader *);
} StdReader;

typedef struct {
    ReaderType type;
    void (*fin)(Reader *);
    int (*get)(Reader *);
    char *str;
    char *next;
} StrReader;

union tagReader {
    ReaderType type;
    AnyReader any;
    StdReader std;
    StrReader str;
};

void stdreader_init(Reader *reader);
void stdreader_fin(Reader *reader);
int stdreader_get(Reader *reader);
void strreader_init(Reader *reader, char *str);
void strreader_fin(Reader *reader);
int strreader_get(Reader *reader);

void stdreader_init(Reader *reader)
{
    reader->type = STD_READER;
    reader->std.fin = stdreader_fin;
    reader->std.get = stdreader_get;
}

void stdreader_fin(Reader *reader)
{
    UNUSED_ARGUMENT(reader);
}

int stdreader_get(Reader *reader)
{
    UNUSED_ARGUMENT(reader);

    return getchar();
}

void strreader_init(Reader *reader, char *str)
{
    reader->type = STR_READER;
    reader->str.fin = strreader_fin;
    reader->str.get = strreader_get;
    reader->str.str = malloc(strlen(str) + 1);
    strcpy(reader->str.str, str);
    reader->str.next = reader->str.str;
}

void strreader_fin(Reader *reader)
{
    free(reader->str.str);
}

int strreader_get(Reader *reader)
{
    char c = *reader->str.next;
    if (c == '\0') return EOF;
    reader->str.next++;
    return c;
}

int main(int argc, char *argv[])
{
    Reader reader;
    int c;

    if (argc > 1) {
        strreader_init(&reader, argv[1]);
    } else {
        stdreader_init(&reader);
    }
    while ((c = reader.any.get(&reader)) != EOF) putchar(c);
    reader.any.fin(&reader);
    return 0;
}

拡張性やメンテナンス性を考えると共用体を利用するこの方法は良くない方法だと思う。
ただ、共通部分の構造体を各構造体の先頭に置く方法よりも、
仕掛けを用意しなくてもそれなりに見通しの悪くないソースが書けると思う。