ライブラリなどを使用していると、「使い方は大体分かった!使ってみよう!」とすると微妙にはまってしまうことがあります。
前回・前々回でお話しした文字コードもその一つですが今回は例外処理について考えてみます。
特に読み込み処理の部分で紹介した、read_xmlでファイルが存在しなかった場合やルート要素が見つからない場合について見てみましょう!
XMLファイルが見つからないヨ!
前々回紹介したXMLファイルの読み込みコードを見てみましょう。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> #include <string> #include <codecvt> #include <boost/property_tree/xml_parser.hpp> int main() { // ロケール設定 std::wcout.imbue(std::locale("ja", std::locale::ctype)); boost::property_tree::ptree pt; boost::property_tree::xml_parser::read_xml("booklist.xml", pt); // UTF-8 を wchar_t に変換するコンバーター std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> cvt; for (auto it : pt.get_child("book-list")) { if (auto isbn = it.second.get_optional<long long>("<xmlattr>.ISBN")) std::wcout << "ISBN : " << isbn << '\n'; if (auto title = it.second.get_optional<std::string>("title")) std::wcout << "TITLE : " << cvt.from_bytes(title->c_str()) << '\n'; if (auto author = it.second.get_optional<std::string>("author")) std::wcout << "AUTHOR : " << cvt.from_bytes(author->c_str()) << '\n'; } return 0; } |
このコードでread_xmlの引数に指定している、”booklist.xml”。
実行ファイルと同じパスに存在していれば問題ありませんが、実行ファイルとは異なる場所にある場合は例外がスローされます。
Debug版ではabort()が呼ばれた。
Release版では動作停止。
そのため、該当部分はtry ~ catch文で処理をしないといけません。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <iostream> #include <string> #include <codecvt> #include <boost/property_tree/xml_parser.hpp> int main() { // ロケール設定 std::wcout.imbue(std::locale("ja", std::locale::ctype)); // utf-8 を wchar_t に変換するコンバーター std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> cvt; try { boost::property_tree::ptree pt; boost::property_tree::xml_parser::read_xml("booklis.xml", pt); for (auto it : pt.get_child("book-list")) { if (auto isbn = it.second.get_optional<long long>("<xmlattr>.isbn")) std::wcout << "isbn : " << isbn << std::endl; if (auto title = it.second.get_optional<std::string>("title")) std::wcout << "title : " << cvt.from_bytes(title->c_str()) << std::endl; if (auto author = it.second.get_optional<std::string>("author")) std::wcout << "author : " << cvt.from_bytes(author->c_str()) << std::endl; } } catch (boost::property_tree::xml_parser_error& e) { std::wcout << L"ERROR : " << e.what() << std::endl; return 1; } return 0; } |
read_xmlでXMLファイルが見つからない場合は、std::runtime_errorから派生したboost::property_tree::xml_parser_error例外クラスがスローされるため、26行目でキャッチしてあげます。
そしてコンソール画面にエラー内容を表示して、エラー終了させると以下のように表示されます。
> ERROR : booklis.xml: cannot open file
booklis.xml…’t’が抜けていますネ!
「booklis.xmlは開けませんでした」と分かりやすいエラー内容になりました。
ルート要素が見つからないヨ!
サンプルにはもう一つ例外が投げられるパスがあります。
16行目の以下の部分です。
0 |
for (auto it : pt.get_child("book-lis")) { |
この部分ではXMLのルート要素を指定してルート要素以下の要素を取得しようとしていますが、ルート要素が間違っている場合、同じくstd::runtime_errorの派生クラス、boost::property_tree::ptree_bad_path例外がスローされます。
0 1 2 3 |
catch (boost::property_tree::ptree_bad_path& e) { std::wcout << L"ERROR : " << e.what() << std::endl; return 1; } |
個別に対応する必要がなければどちらもstd::runtime_errorやその基底クラスstd::exceptionで補足してエラー対応でもいいかもしれませんネ。
まとめ
デバッグ中に実行時エラーが発生してげんなり。くらいならどうってことはないですがリリース後の実行時エラーを考えるとぞっとします。
ライブラリによっては戻り値でエラーか否かの判定ができる場合と例外スローの場合があるのでテスト作業やデバッグ中はわざと間違った引数を指定してみるのも大切です。