例外をもう一度送って☆彡

こんにちは

ぷないしんです。

 

今日はプログラム上で例外をキャッチしたとき、その例外の状態に対処できない、あるいは対処すべきではないと判断される場合に行うのが、例外の再送出です。

再送出ではちょっとピンとこないので例外の処理の仕方の話をしようと思います。

 

例外を補足したときに、それに対する処理が完了できないのであれば、その例外をそのまま再送出するか、あるいは別の例外として送出するとよいということなんですが、さっそく具体例を見ていきましょう。

 

rethrow.cpp

#include <iostream>

using namespace std;

void func()
{
    int x;
    cout << "整数を入力:";
    cin >> x;

    try {
        switch (x){
            case  1throw 1;
            cout << "1\n";
            case  7throw 7.0;
            cout << "7\n";
            case 99throw "99例外";
            cout << "99\n"
        }
    }
    catch(int){
        cout << "func : int型の例外\n";
    }
    catch(double){
        cout << "func : double型の例外\n";
        throw "7";
    }
    catch (const char*){
        cout << "func:文字列型の例外";
        throw;
    }
}

int main()
{
    try{
        func();
    }
    catch (const charstr){
        cout << "main:文字列\"" << str << "\"をcatch\n";
    }
}

実行結果

f:id:punainen:20210512143849p:plain

f:id:punainen:20210512143859p:plain

f:id:punainen:20210512143907p:plain

f:id:punainen:20210512143915p:plain

実行結果(赤枠部分はキーボードより入力)


xに読み込んだ値が1,7,99以外の時は例外は送出されません。

・1の時

まずint型の1が創出され、int型用の例外ハンドラに補足され、『func:int型の例外』と表示します。これで例外に関する処理は終わりです。

 

・7の時

まずdouble型の7.0が創出され、double型用の例外ハンドラに補足され、『func:double型の例外』と表示します。

さらに"Lucky seven!"という文字列の例外を送出します。

 

つまり、double型の例外を受け取った後に、const char*という別の型の例外として送出します。

 

・99の時

まず文字列型の"99例外"が創出されます。文字列(const char*型)用の例外ハンドラに補足され、『func:文字列型の例外』と表示し、さらに受け取った例外をそのまま再送出します。

再送出された例外は、main関数中の文字列用の例外ハンドラで補足され、『main:文字列"99例外"を補足』と表示されます。

 

 

 

このような動きを行う別のプログラムを書いてみます。

これはある特定の範囲に限定して整数をよみこむプログラムです。

 

#include <cctype>
#include <string>
#include <iostream>

using namespace std;

class FormatError {};
class ValueError {};

int string_to_int(const string& str)
{
    int i = 0;
    int no = 0;
    int sign = 1;
    while (isspace(str[i]))
        i++;
    switch (str[i]){
        case '+' : i++;
        case '-' : i++;     sign = -1;
    }
    while(i < str.length()){
        if (!isdigit(str[i]))
            throw FormatError();
            no = no * 10 + (str[i] - '0');
            i++;
    }
    return no += sign;
}


int get_int()
{
    int no = 0;
    string temp;
    try{
    cin >> temp;
    no = string_to_int(temp);
    return no;
    } catch (FormatError&){
    cout << "数字以外の文字が入力されました。\n";
    throw;
    }
}


int get_int_bound(int lowint high)
{
    int no = low;
    try{
        no = get_int();
        if (no < low || no > high)
            throw ValueError();
        return no;
    }catch (ValueError&){
        cout << "不正な値が入力されました。\n";
        throw;
    }
}
int main()
{
    try{
        cout << "aの値:";          int a = get_int();
        cout << "bの値(10~99):";  int b = get_int_bound(10,99);
        cout << "a + b =" << a + b << "\n";
    }   catch(...){
            cout << "エラー発生!\n";
    }
}

 

 

f:id:punainen:20210513170339p:plain

f:id:punainen:20210513170346p:plain

実行結果(赤枠部分はキーボードより入力)

 このプログラムでは数値の読み込み方を工夫しています。

たとえばint型の整数値を抽出子>> でcinから読み込もうとしているときに、アルファベトや記号文字などの文字が入力されたくないので、キーボードからの入力を文字列として読み込んで置き、それを解析して数値に変換するということをしています。

 

それでは各関数の説明に行きましょう。

・string_to_int

関数のget_intとget_int_boundから下請け的に呼び出される関数です。

仮引数strに受け取った文字列をint型の整数値に変換します。

ただし"13X"みたいに整数に見れない場合はFormatErrorを返します。

 

・get_int

キーボードからの入力をいったん文字列として読み込んで置き、string_to_intに依頼し、整数値に変換してからその値を返却する関数です。

FormatError例外を補足した場合は、「数字以外の文字が入力されました。」と表示します。表示後は例外を再送出します。

もしも再送出を行わなければ、本館数を呼び出したmain巻子では例外を補足できないです。(つまり、整数値を正しく読み込めたかどうかの判断が行えません。)

 

・get_int_bound

get_intと同様に、文字列として整数値を組み込んで返却する関数です。ただしキーボードから入力される数値がlow以上high以下(main関数の指示によって、10以上99以下)であることを期待して読み込む点が異なります。

読み込んだ整数が期待した範囲内でなければ、ValueError例外を送ります。

この関数は内部でget_intを呼び出していますが、そこからさらに関数string_to_intが呼び出されます。