[C++] 例外処理のメモ

例外処理に利用するクラスなどはヘッダをインクルードして使う

std::runtime_error 例外処理

C++ の標準の例外は全て std::excpeption の派生クラス(stdexceptヘッダ参照)になっている。

参考 : C++編(標準ライブラリ) 第27章 例外クラス

C++ は例外としてどんな型でも投げられる(ただし void は不可)けども

std::exception の派生クラスを普通は作る。

C++ の例外はスタックトレースが取れない。

またデストラクタから例外を送出してはならない。

void thrower(bool b) throw(int)
{
  if (b)
  {
    throw 100;
  }
  else
  {
    throw "not int";
  }
}

int main()
{
  try
  {
    throw std::runtime_error("TEST");
  } catch (std::runtime_error e) {
    puts(e.what()); // TEST
  }

  try
  {
    throw nullptr;
  }
  catch (...) // どんな型でも受け取れる
  {
    puts("nullptr");
  }

  try
  {
    thrower(true);
  }
  catch (const char *text)
  {
    puts(text); // ここは通らない
  }
  catch (int &i)
  {
    printf("%d\n", i); // 100
  }

  try
  {
    thrower(false); // terminate called throwing an exception
  }
  catch (...)
  {
    puts("ココを通らずにプログラム実行終了");
  }
}

例外仕様の記述が無い場合にキャッチしないと関数外に送出されずに実行終了する。

ただし std::runtime_error は例外仕様を記述する必要がない。

noexcept (C++11)

noexcept は例外仕様で throw() と書いた場合と同じ効果。

noexcept(true) とすると例外を投げうるが、例外の指定はできない。

C++11throw による例外仕様は非推奨となり、代わりに noexpect を使う。

exception_ptr (C++11)

exception_ptr はあらゆる例外を保持できる。

current_exception は現在のcatchブロック内の例外を取得できる。

rethrow_exceptionexception_ptr を再スローする。

make_exception_ptrexception_ptr を生成できる

std::exception_ptr ep;

try
{
  throw 123;
}
catch (...)
{
  // int型などの例外でも current_exception で取得できる
  ep = std::current_exception();
}

try
{
  std::rethrow_exception(ep); // もっかい投げられる
}
catch (int e)
{
  std::cout << e << std::endl;
}

// exception_ptr 自体を作る場合は make_exception_ptr
std::runtime_error e("TEST");
ep = std::make_exception_ptr(e);

try
{
  std::rethrow_exception(ep);
}
catch (std::runtime_error e)
{
  std::cout << e.what() << std::endl;
}

nested_exception (C++11)

nested_exception は名前通り入れ子を保持できる例外であり多重継承の mixin として利用する。

class E : public std::nested_exception {
 public:
  int tag;
  E(int tag) : tag(tag) {};
};

try
{
  throw "TEST";
}
catch (...)
{
  try
  {
    throw E(1);
  }
  catch (E e)
  {
    std::cout << e.tag << std::endl; // 1

    try
    {
      // 内包された例外を送出
      e.rethrow_nested();
    }
    catch (const char * text)
    {
      std::cout << text << std::endl; // TEST
    }

    try
    {
      // .nested_ptr() で内包された例外を取得可能
      std::exception_ptr ep = e.nested_ptr();
      std::rethrow_exception(ep);
    }
    catch (const char *text)
    {
      std::cout << text << std::endl; // TEST
    }
  }
}

//
// catchブロック外で生成してもネストした例外は空
//
try
{
  throw E(2);
}
catch (E e)
{
  if ( ! e.nested_ptr())
  {
    std::cout << "no nested ptr" << std::endl;
  }
}

//
// rethrow_if_nested は
//   nested_ptr であれば中身(ネストされた例外)を送出する
//   ネストした例外が無いとダメ
//
try
{
  std::string foo = "foo";
  std::rethrow_if_nested(foo); // 何もおこらない
  throw foo;
}
catch (std::string &foo)
{
  //
  // throw_with_nested は
  //   引数が nested_ptr であればそのまま送出し
  //   引数が nested_ptr でなければ nested_ptrと引数の型を継承した派生クラスを送出する
  //     なので継承できないクラスは throw_with_nested に渡せない
  //
  try
  {
    // ここで投げられる例外は nested_ptr でも runtime_error でも catch できる
    // foo をネストした例外が投げられる
    std::throw_with_nested(std::runtime_error("bar"));
  }
  catch (std::runtime_error &bar)
  {
    std::cout << bar.what() << std::endl; // bar
    try
    {
      std::rethrow_if_nested(bar);
    }
    catch (std::string &foo)
    {
      std::cout << foo << std::endl; // foo
    }
  }
}
Share
関連記事