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
これは彼らの例を「修正」する正しい方法ですか? か否か?