読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

gtkmmで例外

 gtkmmを使う場合、Gtk::Mainのrunを実行中に例外を投げるとtry-catchで括っていてもcatch出来ずに落ちるみたいです。

#include <exception>
#include <iostream>
#include <gtkmm.h>

void exception_throw() {
  throw std::exception();
}

int main() {
  try {
    Gtk::Main kit(0, nullptr);
    Glib::signal_timeout().connect(&exception_throw, 16);
    Gtk::Main::run();
  } catch (const std::exception &error) {
    std::cout << error.what() << std::endl;
  }
  return 0;
}

実行結果

glibmm-ERROR **: 
unhandled exception (type std::exception) in signal handler:
what: std::exception

aborting...

 このコードを実行してみるとcatch節まで到達せずに、glib側のハンドラに捕まるみたいです。例外を補足したい場合は次のように書くみたいです。

#include <exception>
#include <iostream>
#include <gtkmm.h>

void exception_throw() {
  throw std::exception();
}

void handler() {
  try {
    throw;
  } catch (const std::exception &error) {
   std::cout << error.what() << std::endl;
  }
}

int main() {
  Gtk::Main kit(0, nullptr);
  Glib::add_exception_handler(&handler);
  Glib::signal_timeout().connect(&exception_throw, 16);
  Gtk::Main::run();
  return 0;
}

実行結果

std::exception

 これは結構厄介で、マルチプラットフォームなライブラリをWindows向きにWin32Linux向きにgtkmmみたいな感じでメッセージループを実施する函数を作った場合にWindows向きは普通にメッセージループ函数をtryで括ってcatchで例外が拾えるけどLinux向きはそれが出来ないみたいな残念仕様になってしまいます。なんとかhandlerから外に例外を通知するいい方法はないものですかね。
 取り敢えずどこかにhandlerが捉えた例外を保存しておいて例外が起きたことを知らせるフラグをセット。run終了後にそのフラグを調べて例外があればそれを再送とかも考えたのですが、値で保存するとスライシングで派生例外クラスが残当するのであまりうまくありません。

#include <stdexcept>
#include <iostream>
#include <gtkmm.h>

std::exception exception;
bool throwed = false;

bool exception_throw() {
  throw std::runtime_error("スライスされる運命か・・・");
}

void handler() {
  try {
    throw;
  } catch (const std::exception &error) {
   exception = error;
   throwed = true;
   Gtk::Main::quit();
  }
}

int main() {
  try {
    Gtk::Main kit(0, nullptr);
    Glib::add_exception_handler(&handler);
    Glib::signal_timeout().connect(&exception_throw, 16);
    Gtk::Main::run();
    if (throwed) {
    	throw exception;
    }
  } catch (const std::exception &error) {
    std::cout << error.what() << std::endl;
  }
  return 0;
}

実行結果

std::exception

 そもそもこれだとスライシングうんぬん以前に一部例外だけを切り出して捕捉して処理を分岐とかができないですね。うーむ、どうしたものでしょう。