メインコンテンツへスキップ

C++ Boost::program_optionsでカスタムエラーメッセージを設定、表示する方法

··858 文字·2 分·
プログラミング C++ Boost
著者
Admin
目次

状況
#

🔗boost::program_optionsで不正な引数が渡された際に、詳細なエラーメッセージを表示したい。
ライブラリの例外クラスではエラーメッセージを追加することはできないようだ。

std::exceptionの派生を投げる手もあるが、できればprogram_optionsのエラーメッセージを使いまわしたい。

方法
#

boost::program_options::errorかその派生クラスを更に継承してあげればOK。

boost::program_optionsのエラー(例外)関連クラスはboost/program_options/errors.hpp内で定義されている。

boost::program_options::validation_errorが汎用的でおすすめ。
俺を信じろ。

以下、実装例。
boost::program_options::validation_errorを継承して自前の詳細エラーメッセージを付け加えるようにしただけ。

class invalid_option_error_with_msg
    : public boost::program_options::validation_error {
public:
  invalid_option_error_with_msg(std::string_view const option_name,
                                std::string_view const original_token,
                                std::string_view const error_message)
      : boost::program_options::validation_error(
            boost::program_options::validation_error::invalid_option_value,
            option_name.data(), original_token.data()),
        m_custom_message(error_message) {}

  virtual const char *what() const noexcept override {

    // 派生元のwhat()を呼んでエラーメッセージを取得。
    auto const what = boost::program_options::validation_error::what();

    // 取得した派生元のエラーメッセージに自分のメッセージを追加
    // const char *を返すのでstd::stringが死なないように派生元クラスのメンバであるm_messageにメッセージを格納しておく
    m_message = fmt::format("{}, got {}, detail: {}", what,
                            this->m_substitutions.at("original_token"), // <-- コンストラクタで渡した original_token は内部で格納されているので拝借。
                            m_custom_message);
    return m_message.c_str();
  }

private:
  std::string m_custom_message;
};

使用例
#

 boost::program_options::options_description config("Configuration");
  config.add_options()(
      "server-address,a",
      boost::program_options::value<std::string>()
          ->notifier([](std::string_view address) {
            if (address.starts_with("loc")) {

              // after
              // 継承したクラスを利用
              throw invalid_option_error_with_msg(
                  "server-address", address,
                  "localhost is not allowed, use 127.0.0.1 instead"); // <-- 詳細エラーメッセージ
              
              // before
              throw boost::program_options::validation_error(
                  boost::program_options::validation_error::
                      invalid_option_value,
                  "server-address", address.data());
            }
          })
          ->required(),
      "server address to connect")(
      "server-port,p", boost::program_options::value<int>()->required(),
      "server port");


      // ...

int main(int argc, char** argv){
    // ...
  try {
   // パース

  } catch (boost::program_options::error &e) { // <-- boost::program_options::error から派生したクラスの例外はここでキャッチされる
    spdlog::error("Invalid argument: {}", e.what());
    return EXIT_FAILURE;
  } catch (std::exception &e) {
    spdlog::error("Exception: {}", e.what());
    return EXIT_FAILURE;
  }
    // ...
    }

エラーメッセージ サンプル
#

root@2b7592ccxxxx:/xxxx/build# ./xxxx/client --server-address localhost11451 --server-port 12314
[2024-06-20 03:08:50.969] [error] Invalid argument: the argument for option 'server-address' is invalid, got localhost11451, detail: localhost is not allowed, use 127.0.0.1 instead

いかがでしたか?

Rustのcli引数パーサーライブラリ🔗clapは便利ですね。
C++は苦しいです。

くるC!

Related

C++20 std::chrono::sys_clock, std::chrono::utc_clockのtime_pointを整数型で保持する
··1174 文字·3 分
プログラミング C++ C++20
C++23 std::expected<void, T>でreturnを書かないとsigillが出る罠
··963 文字·2 分
プログラミング C++ C++23
Getter、Setterとは? 必要かどうか迷った場合に考えること
··1588 文字·4 分
プログラミング Rust C++
構造体やクラスのフィールドのpublicやprivateについても解説しています。