3

msdn (リンク) の「チュートリアル: C++ を使用して初めての Metro スタイル アプリを作成する」に取り組んでいます。まもなく「パート 2」に入りますが、エラーが発生します。Visual Studio 2012 Release Candidate (最新) を使用して、Windows 8 VM Release Preview (5 月 31 日) でこれを実行しています。

私がいる場所は、3 つの新しいメトロ ページ、ItemsPage、SplitPage、および新しい「DetailPage」を追加した後のコード セクションです。それらの追加はうまくいきましたが、すぐ下にコードを追加すると、「データモデルを初期化する非同期コードを変更するには」というラベルの付いたセクションで、以下のエラーのコピーが 2 つ作成されます。

error C2893: Failed to specialize function template ''unknown-type' Concurrency::details::_VoidReturnTypeHelper(_Function,int,...)' c:\program files (x86)\microsoft visual studio 11.0\vc\include\ppltasks.h   404 1   SimpleBlogReader

次に、そのセクションからすべてのコードを取り出し、一度に 1 つずつ追加して、「実際に」エラーがどこにあるかを調べました。これは、明らかにその標準ヘッダー ファイルを変更していなかったためです。App::InitDataSourceメソッドのタスク チェーンにあることがわかります。

SyndicationClient^ client = ref new SyndicationClient();   

for(wstring url : urls)
{
    // Create the async operation. 
    // feedOp is an IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
    auto feedUri = ref new Uri(ref new String(url.c_str()));
    auto feedOp = client->RetrieveFeedAsync(feedUri);

    // Create the task object and pass it the async operation.
    // SyndicationFeed^ is the type of the return value
    // that the feedOp operation will eventually produce.       

    // Then, initialize a FeedData object with the feed info. Each
    // operation is independent and does not have to happen on the
    // UI thread. Therefore, we specify use_arbitrary.
    create_task(feedOp).then([this]  (SyndicationFeed^ feed) -> FeedData^
    {
        return GetFeedData(feed);
    }, concurrency::task_continuation_context::use_arbitrary())

        // Append the initialized FeedData object to the list
        // that is the data source for the items collection.
        // This has to happen on the UI thread. By default, a .then
        // continuation runs in the same apartment thread that it was called on.
        // Because the actions will be synchronized for us, we can append 
        // safely to the Vector without taking an explicit lock.
        .then([fds] (FeedData^ fd)
    {
        fds->Append(fd);

        // Write to VS output window in debug mode only. Requires <windows.h>.
        OutputDebugString(fd->Title->Data());
        OutputDebugString(L"\r\n");
    })

        // The last continuation serves as an error handler. The
        // call to get() will surface any exceptions that were raised
        // at any point in the task chain.
        .then( [this] (concurrency::task<SyndicationFeed^> t)
    {
        try
        {
            t.get();
        }
        // SyndicationClient throws E_INVALIDARG 
        // if a URL contains illegal characters.
        catch(Platform::InvalidArgumentException^ e)
        {
            // TODO handle error. For example purposes
            // we just output error to console.
            OutputDebugString(e->Message->Data());
        }
    }); //end task chain

ラムダを一度に 1 つずつ取り出し (そしてセミコロンを入れてコンパイルします)、最初の 2 つがあれば問題ありませんが、チェーンの最後の 1 つはエラーを引き起こします。しかし、私create_taskが最後のものだけなら、それはコンパイルされます。または、最初と3番目でそれを行うと、コンパイルされます。または、最初の 2 つだけで。

問題は2番目のラムダですか? ヘッダー ライブラリは void 戻り値の型で混乱していますか? それとも別のものですか?この理論に基づいて、「最終」ハンドラー宣言を次のように変更しました。

        // The last continuation serves as an error handler. The
        // call to get() will surface any exceptions that were raised
        // at any point in the task chain.
        .then( [this] (concurrency::task<void> t)

これでコンパイルされます。しかし、msdn ( here ) のドキュメントによると、これは正しいですか? そのページには、以下にコピーされた「値ベースの継続とタスクベースの継続」というセクションがあります。

戻り値の型が T であるタスク オブジェクトを指定すると、型 T またはタスクの値をその継続タスクに提供できます。型 T を取る継続は、値ベースの継続と呼ばれます。先行タスクがエラーなしで完了し、取り消されない場合、値ベースの継続が実行のためにスケジュールされます。タイプ task をパラメーターとして受け取る継続は、タスクベースの継続と呼ばれます。タスク ベースの継続は、継続元タスクが取り消されたり、例外がスローされたりした場合でも、常に継続元タスクの終了時に実行がスケジュールされます。その後、task::get を呼び出して、先行タスクの結果を取得できます。先行タスクがキャンセルされた場合、task::get は concurrency::task_canceled をスローします。先行タスクが例外をスローした場合、task::get はその例外を再スローします。

これは、エラー処理の最終的な継続は、最終的な.then継続の型、または元の型でなければならないということcreate_taskですか? それが最終的なものである場合 (上記で で行ったように)、この継続は実際に上記のすべてのエラーを処理しますか、それとも最終呼び出しvoidのエラーのみを処理しますか?.then

これは彼らの例を「修正」する正しい方法ですか? か否か?

4

1 に答える 1

2

問題は、2 番目のラムダから 3 番目のラムダに渡される戻り値の型が必要なことだと思います (FeedData最初のタスクの戻り値の型と 2 番目のパラメーターの型の一致を参照してください)。2 番目のタスクは何も返さないためvoid、正しい選択のようです。3番目を使用してエラーをキャプチャしたいので、concurrency::task<void>(引用に基づいて)使用する必要があります。

また、見積もりに基づいて、前件 (この場合は 2 番目) タスクが失敗した場合、この最終タスクが呼び出され、t.get()が呼び出されたときに実行中に発生したエラーが報告されます。最初のタスクが失敗した場合についてはわかりませんが、最初のタスクから任意の例外をスローすることで何が起こるかを試すことができます。

于 2012-06-07T15:09:36.303 に答える