C++でのstd::futureを使用した例外伝搬の実践ガイド

C++の非同期プログラミングにおいて、std::futureは重要な役割を果たします。特に、非同期タスクで発生する例外を適切に伝搬し、処理することは、信頼性の高いプログラムを作成するために不可欠です。本記事では、std::futureを使用した例外伝搬の仕組みと実践方法について、具体例を交えながら詳しく解説します。初心者から上級者まで役立つ内容となっており、std::futureの基本から応用例までをカバーしています。

目次

std::futureとstd::asyncの基本

std::futureとは

std::futureは、非同期タスクの結果を取得するためのクラスです。非同期タスクの完了を待ち、結果を取得するためのメカニズムを提供します。

std::asyncの概要

std::asyncは、新しい非同期タスクを開始し、その結果をstd::futureで取得できるようにします。std::asyncは、バックグラウンドでタスクを実行し、タスクが完了するまで待機することなくプログラムを進行させることができます。

基本的な使用例

以下は、std::asyncとstd::futureを使った基本的な非同期処理の例です。

#include <iostream>
#include <future>
#include <thread>

int compute()
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模擬的な長時間処理
    return 42;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, compute);

    std::cout << "計算中...\n";

    // 結果を取得(ここでタスクの完了を待機)
    int value = result.get();

    std::cout << "計算結果: " << value << std::endl;

    return 0;
}

このコードでは、compute関数を非同期に実行し、その結果をresultというstd::futureオブジェクトに格納します。メインスレッドは非同期タスクの完了を待たずに進行し、result.get()を呼び出すことでタスクの結果を取得します。

例外伝搬の仕組み

std::futureにおける例外の伝搬

非同期タスクで発生した例外は、std::futureを通じて呼び出し元に伝搬されます。タスク内で例外がスローされると、その例外はstd::futureオブジェクトに格納され、std::future::getを呼び出したときに再スローされます。

例外伝搬の基本的な流れ

  1. 非同期タスク内で例外が発生する。
  2. 例外はstd::futureオブジェクトに保存される。
  3. 呼び出し元がstd::future::getを呼び出すと、例外が再スローされる。

例外伝搬の具体例

以下は、非同期タスク内で例外が発生し、それがstd::futureを通じて伝搬される例です。

#include <iostream>
#include <future>
#include <stdexcept>

int riskyCompute()
{
    throw std::runtime_error("計算エラーが発生しました");
    return 42;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, riskyCompute);

    try
    {
        int value = result.get(); // ここで例外が再スローされる
    }
    catch (const std::exception& e)
    {
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

このコードでは、riskyCompute関数が例外をスローします。std::futureオブジェクトresultはこの例外を捕捉し、result.get()を呼び出すと例外が再スローされ、main関数内のcatchブロックで捕捉されます。

複数の例外ハンドリング

非同期タスク内で複数の例外が発生する場合、それぞれの例外は個別のstd::futureオブジェクトを通じて伝搬されます。呼び出し元は、各std::futureオブジェクトに対してgetを呼び出し、例外を適切に処理する必要があります。

例外の捕捉と処理方法

基本的な例外捕捉の方法

非同期タスク内で例外が発生した場合、std::futureオブジェクトを通じてその例外を捕捉し、適切に処理することが重要です。以下に基本的な例外捕捉の方法を示します。

#include <iostream>
#include <future>
#include <stdexcept>

int riskyCompute()
{
    throw std::runtime_error("計算エラーが発生しました");
    return 42;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, riskyCompute);

    try
    {
        int value = result.get(); // ここで例外が再スローされる
    }
    catch (const std::exception& e)
    {
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

このコードでは、非同期タスク内でスローされた例外がstd::futureオブジェクトに保存され、getメソッドを呼び出すことで例外が再スローされ、catchブロックで捕捉されます。

複数の例外を捕捉する方法

複数の非同期タスクがある場合、各タスクの例外を個別に捕捉する必要があります。以下は複数の非同期タスクの例です。

#include <iostream>
#include <future>
#include <stdexcept>

int task1()
{
    throw std::runtime_error("タスク1のエラー");
    return 1;
}

int task2()
{
    throw std::runtime_error("タスク2のエラー");
    return 2;
}

int main()
{
    std::future<int> result1 = std::async(std::launch::async, task1);
    std::future<int> result2 = std::async(std::launch::async, task2);

    try
    {
        int value1 = result1.get(); // ここで例外が再スローされる
    }
    catch (const std::exception& e)
    {
        std::cerr << "タスク1の例外をキャッチ: " << e.what() << std::endl;
    }

    try
    {
        int value2 = result2.get(); // ここで例外が再スローされる
    }
    catch (const std::exception& e)
    {
        std::cerr << "タスク2の例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

このコードでは、task1task2がそれぞれ例外をスローします。各タスクの結果を取得する際に、個別にtry-catchブロックで例外を捕捉しています。

カスタム例外の捕捉

標準の例外クラス以外にも、独自のカスタム例外を定義して捕捉することも可能です。以下はカスタム例外の例です。

#include <iostream>
#include <future>
#include <stdexcept>

class CustomException : public std::exception
{
public:
    const char* what() const noexcept override
    {
        return "カスタム例外が発生しました";
    }
};

int customTask()
{
    throw CustomException();
    return 0;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, customTask);

    try
    {
        int value = result.get(); // ここで例外が再スローされる
    }
    catch (const CustomException& e)
    {
        std::cerr << "カスタム例外をキャッチ: " << e.what() << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << "他の例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

このコードでは、CustomExceptionというカスタム例外をスローし、それを捕捉しています。カスタム例外と標準の例外の両方を捕捉するために、複数のcatchブロックを使用しています。

実装例:例外を投げる非同期タスク

例外を投げる非同期タスクの基本

非同期タスクで例外を投げる場合、その例外はタスクが実行されるスレッド内で発生し、std::futureオブジェクトを通じて伝搬されます。以下に、例外を投げる非同期タスクの基本的な実装例を示します。

例外を投げる非同期タスクの実装例

次のコードは、非同期タスク内で例外を投げ、その例外を呼び出し元で捕捉する方法を示しています。

#include <iostream>
#include <future>
#include <stdexcept>

void asyncTaskWithException()
{
    // タスク内で例外を投げる
    throw std::runtime_error("非同期タスクでエラーが発生しました");
}

int main()
{
    // std::asyncで非同期タスクを起動し、結果をstd::futureで受け取る
    std::future<void> result = std::async(std::launch::async, asyncTaskWithException);

    try
    {
        // タスクの完了を待ち、結果を取得する
        result.get(); // ここで例外が再スローされる
    }
    catch (const std::exception& e)
    {
        // 例外を捕捉し、エラーメッセージを表示する
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

この例では、asyncTaskWithException関数内でstd::runtime_error例外を投げています。メイン関数では、std::asyncを使用してこのタスクを非同期に実行し、std::futureオブジェクトを通じて結果を取得します。result.get()を呼び出すことで、タスク内で発生した例外が再スローされ、catchブロックで捕捉されます。

詳細な解説

  1. 非同期タスクの作成: std::future<void> result = std::async(std::launch::async, asyncTaskWithException); std::async関数を使用して非同期タスクを起動し、その結果をstd::futureオブジェクトで受け取ります。
  2. 例外の捕捉:
    cpp try { result.get(); // ここで例外が再スローされる } catch (const std::exception& e) { std::cerr << "例外をキャッチ: " << e.what() << std::endl; }
    result.get()を呼び出すことで、タスク内で発生した例外が再スローされます。例外が発生した場合、catchブロックで捕捉し、エラーメッセージを表示します。

ポイント

  • 非同期タスク内で例外を投げても、呼び出し元で例外を適切に処理することで、プログラムのクラッシュを防ぐことができます。
  • 非同期タスクの結果を取得する際に例外が発生する可能性があるため、std::future::getを呼び出す際には常に例外処理を行うことが重要です。

std::future::getの使用方法

std::future::getの基本

std::future::getは、非同期タスクの結果を取得するためのメソッドです。このメソッドを呼び出すことで、非同期タスクが完了するまで待機し、タスクの結果を返します。もしタスク内で例外が発生していた場合、getメソッドはその例外を再スローします。

基本的な使用例

以下は、std::future::getを使用して非同期タスクの結果を取得する基本的な例です。

#include <iostream>
#include <future>

int compute()
{
    return 42;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, compute);

    // 非同期タスクの完了を待ち、結果を取得
    int value = result.get();

    std::cout << "計算結果: " << value << std::endl;

    return 0;
}

この例では、compute関数を非同期に実行し、std::future<int>オブジェクトで結果を受け取ります。result.get()を呼び出してタスクの結果を取得し、出力します。

例外の再スロー

std::future::getを呼び出す際、非同期タスク内で例外が発生していた場合、その例外が再スローされます。これにより、呼び出し元で例外を捕捉して適切に処理することができます。

例外処理の使用例

次に、例外が発生する場合のstd::future::getの使用例を示します。

#include <iostream>
#include <future>
#include <stdexcept>

int riskyCompute()
{
    throw std::runtime_error("計算中にエラーが発生しました");
    return 42;
}

int main()
{
    std::future<int> result = std::async(std::launch::async, riskyCompute);

    try
    {
        // 非同期タスクの結果を取得し、例外が発生した場合は再スローされる
        int value = result.get();
        std::cout << "計算結果: " << value << std::endl;
    }
    catch (const std::exception& e)
    {
        // 非同期タスク内で発生した例外をキャッチ
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

このコードでは、riskyCompute関数が例外をスローします。std::futureオブジェクトresultはこの例外を捕捉し、result.get()を呼び出すと例外が再スローされ、main関数内のcatchブロックで捕捉されます。

std::future::getの注意点

  • ブロッキング: std::future::getは、非同期タスクが完了するまで呼び出し元スレッドをブロックします。長時間のタスクの場合、このブロッキングを考慮する必要があります。
  • 一度だけ呼び出し可能: getメソッドは一度だけ呼び出し可能です。二度目の呼び出しは未定義の動作を引き起こします。
  • 例外の再スロー: 非同期タスク内で発生した例外はgetメソッドで再スローされるため、必ず例外処理を行うことが重要です。

以上が、std::future::getの使用方法とその注意点についての解説です。非同期タスクの結果を安全に取得するために、これらのポイントを理解しておくことが重要です。

std::promiseを使った例外処理

std::promiseとは

std::promiseは、将来の結果を提供するためのオブジェクトであり、その結果をstd::futureを通じて取得できます。std::promiseを使用すると、非同期タスク内で発生した例外を呼び出し元に伝搬させることができます。

std::promiseを使った基本的な例

以下は、std::promisestd::futureを使って例外を伝搬する基本的な例です。

#include <iostream>
#include <future>
#include <stdexcept>

void asyncTask(std::promise<int> prom)
{
    try
    {
        // タスク内で例外が発生
        throw std::runtime_error("非同期タスクでエラーが発生しました");
        prom.set_value(42); // 通常の結果設定
    }
    catch (...)
    {
        // 例外をキャッチし、promiseに設定
        prom.set_exception(std::current_exception());
    }
}

int main()
{
    // promiseオブジェクトの作成
    std::promise<int> prom;
    // futureオブジェクトをpromiseから取得
    std::future<int> fut = prom.get_future();

    // 非同期タスクを起動し、promiseを渡す
    std::thread(asyncTask, std::move(prom)).detach();

    try
    {
        // futureから結果を取得
        int value = fut.get(); // ここで例外が再スローされる
        std::cout << "計算結果: " << value << std::endl;
    }
    catch (const std::exception& e)
    {
        // 非同期タスク内で発生した例外をキャッチ
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

詳細な解説

  1. promiseオブジェクトの作成: std::promise<int> prom; std::promiseオブジェクトを作成し、その結果を格納するために使用します。
  2. futureオブジェクトの取得: std::future<int> fut = prom.get_future(); promからstd::futureオブジェクトを取得します。このfutureは、promiseが設定されるまで待機します。
  3. 非同期タスクの起動: std::thread(asyncTask, std::move(prom)).detach(); std::threadを使用して非同期タスクを起動し、promiseオブジェクトを渡します。detachを使用してスレッドをデタッチします。
  4. 非同期タスク内での例外処理: try { throw std::runtime_error("非同期タスクでエラーが発生しました"); prom.set_value(42); } catch (...) { prom.set_exception(std::current_exception()); } 非同期タスク内で例外が発生した場合、catchブロックでその例外を捕捉し、prom.set_exceptionを使用して例外をpromiseに設定します。
  5. 結果の取得と例外の再スロー:
    cpp int value = fut.get();
    呼び出し元でfutureから結果を取得しようとすると、非同期タスク内で発生した例外が再スローされます。

std::promiseを使う利点

  • 例外の明示的な伝搬: std::promiseを使用することで、非同期タスク内で発生した例外を明示的に呼び出し元に伝搬できます。
  • 結果の設定: タスクの結果を任意のタイミングで設定できるため、柔軟な非同期処理が可能です。
  • 複雑なエラーハンドリング: 非同期タスク内で複数の例外が発生する場合でも、それぞれの例外を適切に捕捉して伝搬できます。

std::promiseを使った例外処理は、非同期プログラミングにおける強力なツールであり、複雑なエラーハンドリングが求められる場面で非常に有用です。

実践例:非同期タスクのエラーハンドリング

複数の非同期タスクの管理

実際のプロジェクトでは、複数の非同期タスクを並行して実行し、それぞれの結果を管理することが一般的です。各タスクの結果を処理し、エラーが発生した場合に適切に対応する方法を示します。

実践的な非同期タスクの実装例

以下に、複数の非同期タスクを実行し、それぞれのタスクの結果を管理する例を示します。この例では、std::asyncを使用して複数のタスクを並行して実行し、例外を含む結果を処理します。

#include <iostream>
#include <vector>
#include <future>
#include <stdexcept>

// 非同期タスクの一つ
int compute1()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 10;
}

// 非同期タスクの一つ
int compute2()
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模擬的な処理時間
    throw std::runtime_error("compute2でエラーが発生しました");
    return 20;
}

// 非同期タスクの一つ
int compute3()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 30;
}

int main()
{
    // 非同期タスクを起動し、std::futureに格納
    std::vector<std::future<int>> futures;
    futures.push_back(std::async(std::launch::async, compute1));
    futures.push_back(std::async(std::launch::async, compute2));
    futures.push_back(std::async(std::launch::async, compute3));

    // 各タスクの結果を処理
    for (auto& fut : futures)
    {
        try
        {
            int result = fut.get(); // タスクの結果を取得
            std::cout << "タスクの結果: " << result << std::endl;
        }
        catch (const std::exception& e)
        {
            // 非同期タスク内で発生した例外を捕捉
            std::cerr << "例外をキャッチ: " << e.what() << std::endl;
        }
    }

    return 0;
}

詳細な解説

  1. 非同期タスクの作成: std::vector<std::future<int>> futures; futures.push_back(std::async(std::launch::async, compute1)); futures.push_back(std::async(std::launch::async, compute2)); futures.push_back(std::async(std::launch::async, compute3)); std::asyncを使用して複数の非同期タスクを起動し、各タスクのstd::futureオブジェクトをベクターに格納します。
  2. 結果の取得と例外の処理:
    cpp for (auto& fut : futures) { try { int result = fut.get(); // タスクの結果を取得 std::cout << "タスクの結果: " << result << std::endl; } catch (const std::exception& e) { std::cerr << "例外をキャッチ: " << e.what() << std::endl; } }
    futureオブジェクトに対してgetを呼び出し、結果を取得します。もしタスク内で例外が発生していた場合、例外が再スローされcatchブロックで捕捉されます。

実践的なポイント

  • 並行処理の効果: 非同期タスクを並行して実行することで、全体の処理時間を短縮できます。例えば、個々のタスクが合計で3秒かかる場合でも、並行処理により全体での実行時間は2秒程度に抑えられます。
  • 例外処理の重要性: 各タスクの結果を取得する際に、例外が発生する可能性を考慮し、適切な例外処理を行うことが重要です。これにより、プログラムの信頼性が向上します。
  • 柔軟なタスク管理: std::futureを使用することで、非同期タスクの結果を柔軟に管理でき、エラーが発生した場合にも適切に対処できます。

このように、複数の非同期タスクを効果的に管理し、例外処理を適切に行うことで、より信頼性の高い非同期プログラムを実装できます。

応用例:複数タスクの例外処理

複数タスクの管理と例外処理

実際のプロジェクトでは、複数の非同期タスクを効率的に管理し、それぞれの例外を適切に処理することが重要です。ここでは、複数のタスクを並行して実行し、各タスクの結果を処理しつつ、発生する可能性のある例外を適切にハンドリングする応用例を示します。

複数の非同期タスクを扱う例

以下に、複数の非同期タスクを並行して実行し、各タスクの結果と例外を処理するコード例を示します。

#include <iostream>
#include <vector>
#include <future>
#include <stdexcept>

// 非同期タスク1: 正常に終了するタスク
int task1()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 100;
}

// 非同期タスク2: 例外をスローするタスク
int task2()
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模擬的な処理時間
    throw std::runtime_error("task2でエラーが発生しました");
    return 200;
}

// 非同期タスク3: 正常に終了するタスク
int task3()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 300;
}

int main()
{
    // 非同期タスクを起動し、std::futureに格納
    std::vector<std::future<int>> futures;
    futures.push_back(std::async(std::launch::async, task1));
    futures.push_back(std::async(std::launch::async, task2));
    futures.push_back(std::async(std::launch::async, task3));

    // 各タスクの結果と例外を処理
    for (auto& fut : futures)
    {
        try
        {
            int result = fut.get(); // タスクの結果を取得
            std::cout << "タスクの結果: " << result << std::endl;
        }
        catch (const std::exception& e)
        {
            // 非同期タスク内で発生した例外を捕捉
            std::cerr << "例外をキャッチ: " << e.what() << std::endl;
        }
    }

    return 0;
}

詳細な解説

  1. 非同期タスクの起動: std::vector<std::future<int>> futures; futures.push_back(std::async(std::launch::async, task1)); futures.push_back(std::async(std::launch::async, task2)); futures.push_back(std::async(std::launch::async, task3)); std::asyncを使用して複数の非同期タスクを起動し、各タスクのstd::futureオブジェクトをベクターに格納します。これにより、タスクの結果を後で取得できるようになります。
  2. 結果の取得と例外の処理:
    cpp for (auto& fut : futures) { try { int result = fut.get(); // タスクの結果を取得 std::cout << "タスクの結果: " << result << std::endl; } catch (const std::exception& e) { std::cerr << "例外をキャッチ: " << e.what() << std::endl; } }
    futureオブジェクトに対してgetを呼び出し、タスクの結果を取得します。タスク内で例外が発生していた場合、getメソッドはその例外を再スローし、catchブロックで例外を捕捉します。

応用的なポイント

  • タスクの並行実行: 複数の非同期タスクを並行して実行することで、全体の処理時間を短縮し、効率的にリソースを利用できます。
  • 柔軟な例外処理: 各タスクで発生する可能性のある例外を個別に処理することで、プログラムの信頼性を高めることができます。
  • 拡張性: このアプローチは、タスクの数や種類に依存しないため、簡単に拡張可能です。必要に応じてタスクを追加したり、例外処理のロジックを変更したりできます。

実際のプロジェクトでの利用例

実際のプロジェクトでは、以下のようなケースでこのような非同期タスクの管理と例外処理が役立ちます。

  • データ処理: 大量のデータを複数のタスクに分割し、並行して処理することで処理時間を短縮。
  • ネットワーク通信: 複数のリモートサーバーに対して非同期にリクエストを送り、レスポンスを待つ。
  • ユーザーインターフェース: GUIアプリケーションで、バックグラウンドで長時間の処理を行いながら、ユーザーインターフェースをブロックせずに応答性を保つ。

このように、複数の非同期タスクを効率的に管理し、各タスクの例外を適切に処理することで、信頼性の高いプログラムを構築することができます。

練習問題:例外伝搬の実装

練習問題1:基本的な非同期タスクの例外処理

以下のコードを完成させて、非同期タスク内で発生する例外を捕捉し、適切に処理してください。

#include <iostream>
#include <future>
#include <stdexcept>

// 非同期タスクの関数
int taskWithException()
{
    // 例外をスローする
    throw std::runtime_error("非同期タスクでエラーが発生しました");
    return 42;
}

int main()
{
    // 非同期タスクを起動
    std::future<int> result = std::async(std::launch::async, taskWithException);

    try
    {
        // タスクの結果を取得
        int value = result.get();
        std::cout << "タスクの結果: " << value << std::endl;
    }
    catch (const std::exception& e)
    {
        // 例外を捕捉し、メッセージを表示
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

練習問題2:複数の非同期タスクの例外処理

以下のコードを完成させて、複数の非同期タスクを並行して実行し、それぞれの例外を捕捉して処理してください。

#include <iostream>
#include <vector>
#include <future>
#include <stdexcept>

// 非同期タスク1: 正常に終了するタスク
int task1()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 100;
}

// 非同期タスク2: 例外をスローするタスク
int task2()
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模擬的な処理時間
    throw std::runtime_error("task2でエラーが発生しました");
    return 200;
}

// 非同期タスク3: 正常に終了するタスク
int task3()
{
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模擬的な処理時間
    return 300;
}

int main()
{
    // 非同期タスクを起動し、std::futureに格納
    std::vector<std::future<int>> futures;
    futures.push_back(std::async(std::launch::async, task1));
    futures.push_back(std::async(std::launch::async, task2));
    futures.push_back(std::async(std::launch::async, task3));

    // 各タスクの結果と例外を処理
    for (auto& fut : futures)
    {
        try
        {
            int result = fut.get(); // タスクの結果を取得
            std::cout << "タスクの結果: " << result << std::endl;
        }
        catch (const std::exception& e)
        {
            // 非同期タスク内で発生した例外を捕捉
            std::cerr << "例外をキャッチ: " << e.what() << std::endl;
        }
    }

    return 0;
}

練習問題3:std::promiseを使った例外伝搬

以下のコードを完成させて、std::promiseを使用して非同期タスク内の例外を伝搬し、呼び出し元で捕捉して処理してください。

#include <iostream>
#include <future>
#include <stdexcept>

// 非同期タスクの関数
void asyncTaskWithPromise(std::promise<int> prom)
{
    try
    {
        // 例外をスローする
        throw std::runtime_error("非同期タスクでエラーが発生しました");
        prom.set_value(42); // 通常の結果設定
    }
    catch (...)
    {
        // 例外をpromiseに設定
        prom.set_exception(std::current_exception());
    }
}

int main()
{
    // promiseオブジェクトの作成
    std::promise<int> prom;
    // futureオブジェクトをpromiseから取得
    std::future<int> fut = prom.get_future();

    // 非同期タスクを起動し、promiseを渡す
    std::thread(asyncTaskWithPromise, std::move(prom)).detach();

    try
    {
        // futureから結果を取得
        int value = fut.get(); // ここで例外が再スローされる
        std::cout << "タスクの結果: " << value << std::endl;
    }
    catch (const std::exception& e)
    {
        // 非同期タスク内で発生した例外をキャッチ
        std::cerr << "例外をキャッチ: " << e.what() << std::endl;
    }

    return 0;
}

練習問題の解答

これらの練習問題を通じて、非同期タスクの例外処理について理解を深めてください。各問題を実装し、動作を確認することで、例外伝搬の仕組みとその処理方法をより実践的に学ぶことができます。

まとめ

C++の非同期プログラミングにおいて、std::futurestd::promiseを使った例外伝搬は非常に重要な技術です。本記事では、これらの基本概念から始まり、実践的な例や応用例までを詳しく解説しました。std::future::getを使った例外の再スローや、std::promiseを利用した柔軟な例外処理方法を理解することで、より信頼性の高いプログラムを作成できるようになります。非同期タスクの管理と例外処理を適切に行うことで、複雑なプログラムでもエラーに強い設計が可能になります。これらの知識を活用して、効率的で堅牢な非同期プログラミングを実現してください。

コメント

コメントする

目次