error message

エラーメッセージを出力する #

toml11は toml::parsetoml::get<T>/find<T>, as_integer() などから ファイル内の位置情報を含んだエラーメッセージを出力します。

例えば、パース時に整数の文法エラーを発見した場合、

[error] bad integer: `_` must be surrounded by digits
 --> internal string at line 64 in file main.cpp
   |
 1 | a = 123__456
   |        ^-- invalid underscore
Hint: valid  : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755
Hint: invalid: _42, 1__000, 0123

あるいは実際に格納されている型と異なる型を要求した場合

[error] toml::value::as_string(): bad_cast to string
 --> input.toml
   |
 1 | a = 123_456
   |     ^^^^^^^-- the actual type is integer

toml11は toml::value からこのようなエラーメッセージを作成する方法を提供します。

この機能を利用することで、TOMLの文法エラーだけでなく、 例えば正の値でなければならないところに負数が現れた場合などの アプリケーション固有のエラーメッセージを、TOMLファイル内の位置を指摘しながら ユーザーに伝えられるということです。

toml::value の位置情報からエラーメッセージを作成する #

toml::value はそれがパースされた位置の情報を持っています。

その情報は toml::source_location にまとめられ、toml::value::location() で取得できます。

const toml::value& a = input.at("a");
const toml::source_location src = a.location();

ファイルを toml::parse でパースした場合、そのTOMLファイル名と行数が保存されています。

toml::parse_str でパースした場合TOMLファイル名はありませんが、代わりに toml::parse_str を呼び出したC++ソースコードのファイル名と行数がTOMLファイル名として保存されています。 このページの最初の例は toml::parse_str から出力された例でした。 ファイル名の部分に注目してください。

詳細は reference を参照してください。

toml::source_location または toml::value とそれに付随するエラーメッセージを toml::make_error_info に渡すことで、エラー情報を構築できます。 これをtoml::format_error に渡すと、エラーメッセージが std::string にフォーマットされます。

const toml::value& a = input.at("a");
if(a.as_integer() < 0)
{
    const toml::error_info err = toml::make_error_info(
            "positive integer is required", // エラーのタイトル
            a, "but got negative value"     // 値の横に書くメッセージ
        );
    std::cerr << toml::format_error(err) << std::endl;
}

これは以下のようになります。

[error] positive integer is required
 --> input.toml
   |
 1 | a = -123456
   |     ^^^^^^^-- but got negative value

最後に補足をつけ足すこともできます。これはインデントされません。

const toml::value& a = input.at("a");
if(a.as_integer() < 0)
{
    const toml::error_info err = toml::make_error_info(
            "positive integer is required",      // エラーのタイトル
            a, "but got negative value",         // 値の横に書くメッセージ
            "Hint: `a` means length of the data" // 補足
        );
    std::cerr << toml::format_error(err) << std::endl;
}
[error] positive integer is required
 --> input.toml
   |
 1 | a = -123456
   |     ^^^^^^^-- but got negative value
Hint: `a` means length of the data

toml::value からファイル内の行を出力できるのは、 パースしたファイルが文字列としてメモリの中に残されているからです。

パースした文字列はその全体が std::shared_ptrtoml::value に共有されています。 コピーしてもファイル文字列全体がコピーされることはありません。 また、そのファイルをパースして構築された toml::value が全てデストラクトされた時点で、 ファイル情報もメモリ上から解放されます。

ですので、アプリケーションで使用する際には、 toml::value を直接保存するのではなく 読み込み中に必要な値を全て取り出して、変換した値を保存した方がよいでしょう。

文字列に色を付ける #

エラーメッセージにはANSIエスケープコードを使って色を付けることができます。

TOML11_COLORIZE_ERROR_MESSAGE をコンパイル時に定義していれば、 toml11の出力するエラーメッセージはデフォルトで色が付くようになります。

そうでない場合は、 toml::color::enable() を呼び出すことにより、それ以降で出力される エラーメッセージには色が付くようになります。 逆に出力先がコンソールではないなどの理由で色をつけたくない場合は、 toml::color::disable() を呼び出してください。 その時点で色が付くようになっているかどうかは、 toml::color::should_color() の返り値で判定できます。

詳細は reference を参照してください。

また、エラーのタイトルやエラーメッセージ、補足にはデフォルトで色が付きませんが、 toml::color にあるマニピュレータを使って色を付けることも可能です。

std::ostringstream oss;
oss << toml::color::red << "but got negative value";

const toml::error_info err = toml::make_error_info(
        "positive integer is required",      // Error title
        a, oss.str(),                        // Message next to the value
        "Hint: `a` means length of the data" // Supplementary message
    );

こちらも、詳細は reference を参照してください。

エラーメッセージのprefixを[error]から変更する #

エラーには種類があり、デフォルトの [error] ではよくない場合もあるでしょう。

toml::format_error では、 toml::error_info の前に std::string を取って、それを [error] の代わりに出力することができます。

例えば、

const toml::value& a = input.at("a");
if(a.as_integer() < 0)
{
    const toml::error_info err = toml::make_error_info(
            "positive integer is required",      // エラーのタイトル
            a, "but got negative value"          // 値の横に書くメッセージ
        );

    std::ostringstream prefix;
    prefix << toml::color::bold << toml::color::yellow << "[warn]";
    std::cerr << toml::format_error(prefix.str(), err) << std::endl;
    return 0;
}
else
{
    return a.as_integer()
}

このようにすると、 [warn] から始まる警告を出力することができます。

他にも、toml::format_error に直接 error_info の構成要素を渡すことで、 [error] なしのエラーメッセージを作成できます。

const toml::value& a = input.at("a");
if(a.as_integer() < 0)
{
    std::cerr << toml::format_error(
            "[warn] positive integer is required",      // エラーのタイトル
            a, "but got negative value"          // 値の横に書くメッセージ
        ) << std::endl;
    return 0;
}
else
{
    return a.as_integer()
}

複数の toml::value を指すエラーメッセージを作成する #

アプリケーションの設定では、先に読み込んだ値によって後に読み込んだ値が取れる範囲が変わることがあるでしょう。

そのような場合には、エラーの原因となる別の値を同時に出力したいはずです。

toml::format_errortoml::make_error_info は、 toml::value とそれに対応するエラーメッセージ std::string のペアを任意個取ることができます。

std::cerr << toml::format_error(
        "[error] invalid range",
        a, "minimum value is defined here",
        b, "maximum value is defined here",
        c, "and it exceeds the range"
    ) << std::endl;

こちらも末尾に補足を追加することができます。

std::cerr << toml::format_error(
        "[error] invalid range",
        a, "minimum value is defined here",
        b, "maximum value is defined here",
        c, "and it exceeds the range",
        "Hint: all the value must be in the range, [a, b)"
    ) << std::endl;

toml::value または toml::source_location を渡した場合、必ずそれに関する エラーメッセージが続く必要があります。 そうしない場合、非常にわかりにくいコンパイルエラーになります。