C言語の復習をちょっとやってみました。関数へのポインタとかなつかしいです。関数へのポインタは構造体のメンバになれるんだっけ、と思ったので試してみました。  まずはヘッダファイルのkaeru_string.h。

#ifndef _INCLUDE_KAERU_STRING_
#define _INCLUDE_KAERU_STRING_
 
typedef struct kaeru_string {
        int (*str_set)();
        char *(*str_put)();
} *String;
 
extern String str_new(void);
extern void str_free(String);
 
#endif

 次にkaeru_string.c。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kaeru_string.h"
 
static int string_set(const char *);
static char *string_put(void);
 
static char *data;
 
String str_new(void)
{   
    String  new;
 
    new = (String)malloc(sizeof(String));
    new->str_set = string_set;
    new->str_put = string_put;
    data = NULL;
 
    return new;
}
 
void str_free(String self)
{   
    free(data);
    data = NULL;
    free(self);
    self = NULL;
}
 
static int string_set(const char *source)
{   
    int size;
 
    size = strlen(source);
    if (size < 0) {
        return -1;
    }
 
    data = (char *)malloc(size + 1);
    strcpy(data, source);
 
    return 0;
}
 
static char *string_put(void)
{
    return data;
}

 そしてmain.c。

#include <stdio.h>
#include "kaeru_string.h"
 
int main(void)
{   
    String a;
 
    a = str_new();
 
    if (a->str_set("I'm C Programmer!")) {
        printf("error\n");
        return -1;
    }
 
    printf("%s\n", a->str_put());
 
    str_free(a);
 
    return 0;
}

 結果は

I'm C Programmer!

 クラスみたいなものが作れないのかな、と思ったので関数へのポインタを構造体のメンバにしてみたのだけど、すぐにおかしいことに気づいた。main.cを

int main(void)
{
    String a;
    String b; 
 
    a = str_new();
    b = str_new();
 
    if (a->str_set("I'm C Programmer!")) {
        printf("error\n");
        return -1;
    }
 
    if (b->str_set("And I'm Ruby programmer!!")) {
        printf("error\n");
        return -1;
    }
 
    printf("%s\n", a->str_put());
 
    str_free(a);
    str_free(b);
 
    return 0;
}

こうすると結果は、

And I'm Ruby programmer!!

 シングルトンみたいになってしまった。外部変数のchar *dataはただひとつの変数なのだから当然だけど。さらにfreeでdataを何度も開放していることになってしまっていてヤバそう。  そういえば、static指定の関数でも、関数へのポインタ経由なら他ファイルからもアクセスできてしまうみたい。static指定の情報まではポインタに引き継がれないんだね。当然と言われれば当然なのだろうけど。

 シングルトンはイヤなので、dataを構造体の中にいれてみよう。こっちのほうがクラスっぽくて自然だろうし。  と思ったけどすぐにダメなことに気づいた。構造体の中のdataを指すためにStringへのポインタ自体を関数の引数にしなくちゃいけなくて、結局構造体の中へ関数へのポインタを入れる意味がなくなっちゃうのです。つまりは、薄々感じていた平凡な形になってしまうわけです。kaeru_string.h, kaeru_string.c, main.cの順に並べると、

#ifndef _INCLUDE_KAERU_STRING_
#define _INCLUDE_KAERU_STRING_
 
typedef struct kaeru_string {
        char *data;
} *String;
 
extern String str_new(void);
extern void str_free(String);
extern int string_set(String, const char *);
extern char *string_put(String);
 
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kaeru_string.h"
 
String str_new(void)
{   
    String  new;
 
    new = (String)malloc(sizeof(String));
    new->data = NULL;
 
    return new;
}
 
void str_free(String self)
{   
    free(self->data);
    self->data = NULL;
    free(self);
    self = NULL;
}
 
int string_set(String self, const char *source)
{   
    int size;
 
    size = strlen(source);
    if (size < 0) {
        return -1;
    }
 
    self->data = (char *)malloc(size + 1);
    strcpy(self->data, source);
 
    return 0;
}
 
char *string_put(String self)
{   
    return self->data;
}
#include <stdio.h>
#include "kaeru_string.h"
 
int main(void)
{
    String a;
    String b;
 
    a = str_new();
    b = str_new();
 
    if (string_set(a, "I'm C Programmer!")) {
        printf("error\n");
        return -1;
    }
 
    if (string_set(b, "And I'm Ruby programmer!!")) {
        printf("error\n");
        return -1;
    }
 
    printf("%s\n", string_put(a));
    printf("%s\n", string_put(b));
 
    str_free(a);
    str_free(b);
 
    return 0;
}

結果は

I'm C Programmer!
And I'm Ruby programmer!!

 とりあえず形にはなったけど、なんだかクラスっぽさはなくなったね。本来ならprivateにしたいchar *dataにアクセスできちゃうし、そのデータと関数との関連も疎になった感じ。C言語でクラスみたいなものを作ろうというのが変なのかも。

 追記です。

typedef struct kaeru_string {
        char *data;
} *String;

 これってもう構造体の意味がなかったね。ただのcharポインタでOKです。