この記事のポイント

  • 例外再スロー(rethrow)設計での情報補足責務をレビューアーが読み取る視点を整理
  • 上流伝播時の文脈情報付加戦略をレビューで具体的に指摘できるようになる
  • 障害調査容易性を高めるrethrow設計パターンを実務観点で整理

そもそも例外再スロー時の情報補足とは

C++で例外が発生した場合、そのまま上位に伝播させるだけでは
「どの経路で失敗に至ったか」
が見えなくなるケースが多い。

  • 直接throw: 単層原因はわかる
  • 多層rethrow: 経路情報が消失しやすい

再スロー時に文脈情報を上乗せすることで、
障害診断やログ上の原因特定速度が大きく改善される。

なぜこれをレビューするのか

  • 生のままrethrow → 調査現場で経路推測困難
  • catch → logだけ → throw; → 情報断絶
  • 障害現場で「誰がどの処理で再throwしたのか」を残す必要がある

レビューアーは「このrethrowは文脈付加されているか?」を確認すべき。

レビューアー視点

  • どの層の責務で情報を付加すべきか
  • 追加情報の粒度が診断支援になっているか
  • スタックトレース非依存でも経路が辿れるか
  • 再スロー時の新旧例外情報整理が適切か
  • 副作用順序に矛盾しないか

開発者視点

  • catch → log → throw; は最低限
  • 可能なら例外型を差し替え+元例外埋込する
  • ドメイン固有分類名詞化の補助を行う
  • 複数層で「階層的ラップ」を設計する

良い実装例

基本パターン:文脈補足再スロー

文脈情報を上乗せ
#include <stdexcept>
#include <string>

class Repository {
public:
    void save() {
        throw std::runtime_error("DB failure");
    }
};

class Service {
public:
    void process() {
        try {
            repository_.save();
        } catch (const std::exception& e) {
            throw std::runtime_error(std::string("Service::process failed - ") + e.what());
        }
    }
private:
    Repository repository_;
};
  • 上位層名を追加
  • 再throw後も経路が判読可能

発展パターン:独自例外型に文脈再整理

独自型で責務移譲
class ServiceException : public std::runtime_error {
public:
    explicit ServiceException(const std::string& msg)
        : std::runtime_error(msg) {}
};

class Service {
public:
    void process() {
        try {
            repository_.save();
        } catch (const std::exception& e) {
            throw ServiceException(std::string("Process failed: ") + e.what());
        }
    }
private:
    Repository repository_;
};
  • catch側はServiceExceptionのみ捕まえれば良くなる
  • 責務階層で分類整理が進む

レビュー観点

  • 例外型は責務名詞化されているか
  • 元例外のwhat()情報が保持されているか
  • 層名・関数名・引数など経路情報が埋め込まれているか
  • 単なるlog+throw;で満足していないか
  • 障害現場で調査可能な文脈情報量になっているか

良くない実装例: ケース1(再スロー情報補足無し)

何も補足しない再スロー
void service() {
    try {
        repository.save();
    } catch (const std::exception& e) {
        throw;
@Reviewer
再スローするならService層での文脈情報を補足してください。責務境界が読めなくなります。
} }

改善例

改善例(文脈付加)
void service() {
    try {
        repository.save();
    } catch (const std::exception& e) {
        throw ServiceException(std::string("service() failure: ") + e.what());
    }
}

良くない実装例: ケース2(logだけして放流)

ログ出力のみで放流する例
void service() {
    try {
        repository.save();
    } catch (const std::exception& e) {
        logger.error("Repository failed: {}", e.what());
        throw;
@Reviewer
ログだけではなく、再スロー側にも文脈付加してください。調査時に検索可能性が上がります。
} }

改善例

改善例(ログ+文脈付加)
void service() {
    try {
        repository.save();
    } catch (const std::exception& e) {
        logger.error("Repository failed: {}", e.what());
        throw ServiceException(std::string("service() failure after repository: ") + e.what());
    }
}

良くない実装例: ケース3(型継承放棄)

元例外情報断絶型
class FatalError {
public:
    FatalError(const std::string& msg): msg_(msg) {}
    std::string what() const { return msg_; }
private:
    std::string msg_;
};

%% @Reviewer std::exception系継承を行ってください。汎用catch不能になります。また再スロー時には旧例外の情報も保持すべきです。

改善例

改善例(標準継承接続+文脈付加)
class FatalApplicationError : public std::runtime_error {
public:
    FatalApplicationError(const std::string& ctx, const std::exception& cause)
        : std::runtime_error(ctx + ": " + cause.what()) {}
};

PlantUML:rethrow情報補足設計フロー

UML Diagram

ロギング統合例

再スロー文脈を統合出力
try {
    service.process();
} catch (const ServiceException& e) {
    logger.error("Service layer failed: {}", e.what());
} catch (const std::exception& e) {
    logger.fatal("Unhandled exception: {}", e.what());
}
  • 再throw情報はwhat()統合管理で出力

観点チェックリスト

まとめ

レビューアーはrethrow設計時に常に問うべきは
「上流に伝える情報は充分か?」
です。

  • 再現条件:入力情報を埋め込む
  • 経路責務:層名・関数名を埋め込む
  • 原因情報:旧例外のwhat()保持

文脈付加設計は"現場支援型レビュー"の最大テーマです。
レビュー現場では「再スロー時の情報補足設計を整理しましょう」と具体指摘できる技術力が求められます。