1

バックトレース、行番号、ファイル名などを保存するルーチンを作成しました。これの目的は、例外がスローされるたびにそのようなデータを保存することでした。ただし、私が直面している問題は、私のルーチンが catch ブロックから呼び出され、catch ブロックまでのバックトレースが格納されることです。これは良くない。例外がスローされる場所までバックトレースを追加するだけです。私はできません(例外がスローされない場合でもバックトレースを保存することになるため、tryブロック内で呼び出すことは明らかです)。私もいつでもできましたバックトレースを try ブロックの最後に保存し、catch ブロック内でアクセスします。ただし、try ブロックのどの行で例外がスローされるかを知る方法はありません。したがって、スロー関数は、ルーチン呼び出しを追加するのに適した場所のようです。しかし、私はそれを行う方法がわかりません。私を助けてください。

私の戦略が本質的に間違っていると思われる場合は、より良い解決策を教えてください。問題自体が明確でない場合は、コメントを残してください。

PS std::runtime_error から継承するカスタム例外クラスを作成しました。

4

3 に答える 3

4

オーバーライドできる C++ で定義された「スロー関数」はありません。スローは C++ 実装によって処理され、あらゆる種類のコードを挿入する標準的な方法はありませんthrow

代わりに、例外タイプが構築されたときに現在のバックトレースを格納するようにすることができます。

std::string get_backtrace() {
    return "this is the backtrace...";
}

struct backtrace_exception : public std::exception {
    std::string b;

    backtrace_exception() : b(get_backtrace()) {}
};

int main() {
    try {
        throw backtrace_exception();
    } catch(backtrace_exception &e) {
        std::cout << e.b;
    }
}
于 2013-06-05T18:03:16.750 に答える
1

throw 演算子をオーバーロードすることはできません。より一般的な解決策は、バックトレース レコードを使用して例外をパッケージ化するマクロを定義することです。たとえば、次のようになります。

#include <string>
#include <iostream>
#include <sstream>
#include <exception>
#include <stdexcept>
#include <type_traits>

template <typename BaseException>
class backtraced_exception : public BaseException {
  private:
    std::string backtrace;
  public:

    template <typename... Args>
    backtraced_exception(const char* aFilename, int aLineNum, Args&&... args) :
      BaseException(std::forward<Args>(args)...) {
      std::stringstream ss;
      ss << "From '" << aFilename << "' at line " << aLineNum << ":\n" 
         << BaseException::what();
      backtrace = ss.str();
    };

    backtraced_exception(const std::exception& e, const char* aFilename, int aLineNum) :
      BaseException(static_cast<const BaseException&>(e)) {
      std::stringstream ss;
      ss << "From '" << aFilename << "' at line " << aLineNum << ":\n" 
         << e.what();
      backtrace = ss.str();
    };

    virtual ~backtraced_exception() noexcept { };

    virtual const char* what() const noexcept {
      return backtrace.c_str();
    };
};

#define THROW_WITH_BACKTRACE(EXCEPTION, ARG1) throw backtraced_exception< EXCEPTION >(__FILE__, __LINE__, ARG1)
// ... and you can create more macros for more arguments...

#define CATCH_WITH_BACKTRACE(EXCEPTION, EXCEPT_NAME) catch(backtraced_exception< EXCEPTION >& EXCEPT_NAME)

#define RETHROW_WITH_BACKTRACE(EXCEPT_NAME) throw backtraced_exception< std::decay< decltype(EXCEPT_NAME) >::type >(EXCEPT_NAME, __FILE__, __LINE__)

次のように使用します。

int main() {
  try {
    try {
      try {
        THROW_WITH_BACKTRACE(std::runtime_error, "This is an example!");
      } CATCH_WITH_BACKTRACE(std::runtime_error, e) {
        std::cout << "First caught this exception:\n" << e.what() << std::endl;
        RETHROW_WITH_BACKTRACE(e);
      };
    } catch(std::runtime_error& e) {  // can also catch normally.
      std::cout << "Got this exception:\n"
                << e.what() << std::endl;
      // and even rethrow again, with backtrace:
      RETHROW_WITH_BACKTRACE(e);
    };
  } catch(std::runtime_error& e) {
    std::cout << "Finally, got this exception:\n"
              << e.what() << std::endl;
  };
};

出力は次のとおりです。

First caught this exception:
From 'logged_except.cpp' at line 50:
This is an example!
Got this exception:
From 'logged_except.cpp' at line 53:
From 'logged_except.cpp' at line 50:
This is an example!
Finally, got this exception:
From 'logged_except.cpp' at line 59:
From 'logged_except.cpp' at line 53:
From 'logged_except.cpp' at line 50:
This is an example!

このソリューションのもう 1 つの利点は、バックトレースが必要かどうか (デバッグまたはリリース ビルドなど) に応じてマクロを条件付きで定義するだけで、バックトレースを無効にできることです。

上記の例は C++11 の機能を必要としますが、それらの機能 (つまり、可変個引数テンプレート、decltype、type-traits など) がなくても、同等のソリューションを思いつくことができるでしょう。

また、C++11 例外ポインターを使用して、このスキームをさらに便利にすることもできます。

于 2013-06-05T19:12:21.150 に答える