C言語関係掲示板

過去ログ

No.84.例外を使いたい


前に読んだ「CプログラマのためのC++入門」っていう腹の立つ本の中に、
「C++ソースはCソースに変換されてCコンパイラで処理される」
といったようなことが書いてありました。
ということは、例外処理等もCでも実装できるということでしょうか?
setjmp、longjmpを使えばできそうな気がしていろいろ考えてはみたもののいい考えが浮かびません。
マクロを使ってなんとかC++に近い構文で例外処理できるようにならないでしょうか?


試行錯誤してなんとかそれらしくなってきました。
が、catchの処理で詰まってしまいました。
型によって分岐ってどうやったらいいものでしょうか?
longjmpのときに型を保存して、、ってそんなことできないですし。
アドバイス等お願いいたします。

#include <stdio.h>
#include <setjmp.h>

jmp_buf jumper;
int ret;

#define Try                             \
        if ( (ret = setjmp(jumper)) == 0 ) {

#define Catch(type, e)                  \
        } else if ( 1 ) {                       \
                type e = (type)ret;

#define EndCatch                                \
        }

#define Throw(e)                                \
        longjmp(jumper, (int)(e))

/*------------------------------*/
int test_func(int a, int b)
{
        if ( b == 0 )
                Throw("0除算");
        return a / b;
}

int main(void)
{
        int a = 10;
        int b = 0;

        Try {
                int c = test_func(a ,b);
                printf("%d / %d = %d\n", a, b, c);
        } Catch(char *, e) {
                printf("例外発生: %s\n", e);
        } EndCatch

        return 0;
}


今晩は。

>が、catchの処理で詰まってしまいました。
>型によって分岐ってどうやったらいいものでしょうか?

eが文字列でも数値でも使えるようにするってことですか。

chu-さんのプログラム、別におかしくは思いませんでした。
Throw("0除算");としているので、typeにchar *を渡す、
で問題ないように思えました。

もし、eを文字列と数値と切替えて使うんでしたら、
Throwでlongjmpを呼ぶ前に、グローバルエリアに型の情報を記憶して、
Catchでそのグローバルを参照する、なんていうのは姑息ですか。

あと、
#define Catch(type, e)                  \
        } else if ( 1 ) {                       \
                type e = (type)ret;
は、
#define Catch(type, e)                  \
        } else {                                \
                type e = (type)ret;

ではまずいんですか。


返信ありがとうございます。 文章にするのヘタクソなんです。うまく伝わらなかったらすいません。 > eが文字列でも数値でも使えるようにするってことですか。 Tryブロックでは複数種類の例外が発生するのでCatchハンドラは複数書けないくてはいけないです。 --- Try { hoge(); } Catch(char *, e) { 例外処理; } Catch(int, e) { 例外処理; } Catch(TException *, e) { 例外処理; free(e); } EndCatch --- なので、 > } else if ( 1 ) { \ 今(1)となっている部分には(送られてきた例外オブジェクトの型 == このCatchが対応する型) という条件を入れたいと考えているのですが、型の判別ができなくて悩んでいます。 > Throwでlongjmpを呼ぶ前に、グローバルエリアに型の情報を記憶して、 > Catchでそのグローバルを参照する、なんていうのは姑息ですか。 やりたいことはまさにこれです。 型の情報を記憶ってできるんですか? 型って値でもなんでもないので変数に入れとくこともできなくて困ってます。


>> Throwでlongjmpを呼ぶ前に、グローバルエリアに型の情報を記憶して、
>> Catchでそのグローバルを参照する、なんていうのは姑息ですか。

> やりたいことはまさにこれです。
> 型の情報を記憶ってできるんですか?
> 型って値でもなんでもないので変数に入れとくこともできなくて困ってます。

単純に、Throwでlongjmpを呼ぶ前に、グローバルエリアに
char * なら 1 、int なら 2 のように定数で記憶し、Catchでは
switchで振り分ける方法しか思いつかなかったです。
だから、姑息かなと・・・。


> 単純に、Throwでlongjmpを呼ぶ前に、グローバルエリアに
> char * なら 1 、int なら 2 のように定数で記憶し、Catchでは
> switchで振り分ける方法しか思いつかなかったです。
> だから、姑息かなと・・・。
新しく定義した型を投げようとしたときにソース改変が必要になるのは避けたいです。
気軽に使えるものにしたいもので。。

週末いろいろ考えてみて、#を使えば文字列化できるのを思い出しました。
Throwで型名を保存してChatchで比較することによってうまいこと判断できました。
でもstrcmpだとちょっと問題あるのでホワイトスペースを無視するものを定義しようと思います。
細かい問題はありそうですけどとりあえずこれでよしとすることにします。

ネストとか返り値ゼロの場合とかまだいろいろありますが、実装法が思いつかなかった部分はこれで解決できました。
残りもさっさと仕上げてエラー処理で楽しようと思います。

相談にのっていただいてありがとうございました。

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

typedef { false, true } bool;

jmp_buf jumper;
const char *type_name;

#define Try                                             \
        {                                               \
                int ret;                                        \
                if ( (ret = setjmp(jumper)) == 0 ) {

#define Catch(type, e)                                  \
                } else if ( strcmp(type_name, #type) == 0 ) {   \
                        type e = (type)ret;

#define CatchAll()                                              \
                } else if ( true ) {

#define EndCatch                                                \
                } else {                                        \
                        exit(1);                                \
                }                                       \
        }

#define Throw(type, e)                                  \
        {                                               \
                type_name = #type;                              \
                longjmp(jumper, (int)(e));                      \
        }

/*------------------------------*/
int test_func(int a, int b)
{
        if ( b == 0 )
                Throw(char *, "0除算");
        return a / b;
}

int main(void)
{
        int a = 10;
        int b = 0;

        Try {
                int c = test_func(a ,b);
                printf("%d / %d = %d\n", a, b, c);
        } Catch(char *, e) {
                printf("例外発生: %s\n", e);
        } EndCatch

        return 0;
}


>相談にのっていただいてありがとうございました。

そうですか、#演算子ですか。
却って、勉強させていただきました。
chu-さんに比べると、まだまだ知識が足りませんね。


>ともじさん
なんだか自己解決してしまって申し訳ありませんでした。
掲示板投稿のために文章を用意してる間に考えが整理されることがよくあります。
この掲示板のおかげでかなり助かってます。ありがとうございます。
おかげでネストや送出値ゼロにも対応できてなんとか完成しました。
さっそく次回プログラムするときから使っていこうと思ってます。
結構デバッグしましたが、まだどこか変なとこありましたら指摘お願いいたします。

↓あとはヘッダに整理するだけVer
http://www.geocities.jp/chu900rr/program/source/exception.c

戻る


「初心者のためのポイント学習C言語」 Last modified:2002.01.11
Copyright(c) 2000-2002 TOMOJI All Rights Reserved