029. C++の例外をさらっと

送受信をさらっとやったので,前回までにすっとばした例外をやっておく.

図書館にC++プログラミングというぶっとい本があったので試しにこいつを参考にしていく.

C++の例外の構文は

#include <iostream>

#include <exception>

using namespace std;

int main (int argc, char * const argv[]) {

    try{

        throw exception();

    }catch(exception e){

        cout << “catch” << endl;

    }    

    return 0;

}

という感じで,try -> throw -> catchになっている.
どの言語もだいたいこんな感じである.

throwでは何でも投げれるので

    try{

        throw 100;

    }catch(int e){

        cout << e << endl;

    }

とやると,100となる.

で,C++が投げる全ての例外はexception Classを継承しているそうで
階層構造は
http://www.geocities.jp/ky_webid/cpp/library/027.html
こんな感じらしい.

さて,例外はそもそも何のために使うのか.

C++プログラミングの長い文を要約すると

エラー処理のコードをプログラムコードのところどころに書くと,エラーの生じたところで処理できて判断しやすいが,その反面,アプリケーションのコードが「汚染」されてしまう.
だもんで,エラー処理のコードを分離したいので例外を使う

ということになる.

Something *something = new Something();

something->hoge();

something->moge();

….

というコードがあったとして,hogeもmogeもエラーが発生するやもしれない.
hogeとmogeのなかでごちゃごちゃエラー処理するんじゃなくて,

try{

    Something *something = new Something();

    something->hoge();

    something->moge();

    ….

}catch(exception e){

    エラー発生したんで処理します(e)!

}

としておけば,エラー処理を分離できる!

という風に解釈しておく.
たしかに分離できてコード見やすいし,エラー処理部分をまとめられるのでナイス.

では,どういう時に例外処理を使うべきなのか.
作法が書いてあるので引用.

  1. 起きた場所とは異なるスコープで処理しなければならないエラーに対して例外を使ってください.起きた場所と同じスコープ内で処理しうるエラーには,他のエラー処理方法を使うこと
  2. プログラムがわかりにくくなるのを避けるため,エラー処理以外の目的で例外処理を使わないこと

なるほど.

そもそも問題のコードは
SC_ComPort.cpp: 287

if ((mSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

    throw std::runtime_error(“failed to create udp socket\n”);

}


である.
これは,socketが失敗したときは-1が返ってくるのでそれを例外として処理している.

まず,std::runtime_errorとは何か

stdexcept: 109

  class runtime_error : public exception 

  {

    string _M_msg;

  public:

    /** Takes a character string describing the error.  */

    explicit 

    runtime_error(const string&  __arg);

    virtual 

    ~runtime_error() throw();

    /** Returns a C-style character string describing the general cause of

     *  the current error (the same string passed to the ctor).  */

    virtual const char

    what() const throw();

  };

となっている.ふむ.

what()で,引数のmessageが取得できるらしい.

socketが失敗する(であろう)コードを書いてみる.

アホなコードなので実行しないほうがいいかもです.

#include <iostream>

#include <exception>

#include <sys/socket.h>

#include <stdexcept>

using namespace std;

int main (int argc, char * const argv[]) {

    int count = 0;

    try{

        int mSocket;

        while(1){

            if ((mSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

                throw std::runtime_error(“failed to create udp socket\n”);

            }

            count++;

        }

    }catch(exception &e){

        cout << e.what() << endl;

        cout << “count = “ << count << endl;

    }

    return 0;

}

僕の環境では,2558回目でsocketに失敗する.

failed to create udp socket

count = 2557

となる.
こんなにsocketが必要なことも無いだろうが,ともあれ失敗することもあることが判明.

で,scsynthの場合,このsocketが失敗すると,OSCが受け取れずプログラムとしては「致命傷」なわけで
ここでexit()してもいいぐらいだと思うのですが,ここでexit()しちゃうとそこで終り.
もう一度socket()を実行してみたくなったときや,エラーに応じてユーザーにメッセージを出すなんてことができません.

なので,ここでの処理としては
「エラー発生しましたよとお知らせする」
ことにしているのだと思われる.

ということは,必ずこの前の段階でtryがあり,その後にはcatchがあるはず.
コードを追ってみると

SC_World.cpp: 673

void World_OpenUDP(struct World *inWorld, int inPort)

{

    try {

        new SC_UdpInPort(inWorld, inPort);

    } catch (std::exception& exc) {

        scprintf(“Exception in World_OpenUDP: %s\n”, exc.what());

    } catch (…) {

    }

}


でcatchされておりました.

こういう風に書き換えてみると分かりやすい.
SC_ComPort.cpp: 284

SC_UdpInPort::SC_UdpInPort(struct World *inWorld, int inPortNum)

    : SC_ComPort(inWorld, inPortNum)

{    

    //必ず例外を投げる

    throw std::runtime_error(“failed to create udp socket\n”);

    printf(“____below”);

で,throwされた場合,その後のコードは実行されないので,goto的な役割もあるわけですね.
socket()がエラーなのに,bind()等を実行しても無駄ということ.
(____belowはprintされない)

しかし,「致命傷」と思いきや,例外が発生しても

Exception in World_OpenUDP: failed to create udp socket

SuperCollider 3 server ready..


と出た.:)
これぐらいの例外じゃ終了はしないらしい.

catchだけ見るとこれだけしかないし,これぐらいやっとけば大丈夫でしょう.
sc_29.png

このエントリーをはてなブックマークに追加

Leave a Reply

You must be logged in to post a comment.