Я абсолютный нуб, когда дело доходит до использования задач PPL в среде C ++, поэтому мне трудно понять, каким будет синтаксис C ++ следующего кода C #:
private static async Task<RandomAccessStreamReference> GetImageStreamRef()
{
return RandomAccessStreamReference.CreateFromStream(await GetImageStream());
}
private static async Task<IRandomAccessStream> GetImageStream()
{
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, width, height, 96, 96, imageBytes);
await encoder.FlushAsync();
return stream;
}
Этот код C # был взят из Windows Store реверс Microsoft пример кода. Лучшее, что я мог получить, это:
Concurrency::task<IRandomAccessStream^> GetImageStream()
{
auto stream = ref new InMemoryRandomAccessStream();
task<BitmapEncoder^>(BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, Stream)).then([this, stream, width, height, imageBytes](BitmapEncoder^ encoder)
{
encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 96.0, 96.0, imageBytes);
return encoder->FlushAsync();
}).then([this, stream]()
{
return stream; //Does this even make sense?
});
//return stream; //Not sure if I should have this here?
}
Но он генерирует следующую ошибку компиляции:
error C4716: 'GetImageStream' : must return a value
Я понимаю, почему происходит эта ошибка, но я понятия не имею, как у меня может быть функция, которая возвращает задачу, не имея возвращаемого значения в двух разных местах? Я еще даже не занимался GetImageStream.
Я даже не уверен, что выбрал правильный путь в этом …
Спасибо!
Ты очень близко. Ключевым моментом, который вы можете упустить, является то, что then
возвращает вас новая задача. Итак последнее then
в цепочке определяет тип вашей задачи.
auto t = task<int>([] { return 0; });
// t is task<int>
auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; });
// t is task<double>
auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; })
.then([](double d) { return "foo"; });
// t is task<const char*>
Если вы просто посмотрите на первую строку, похоже, у вас всегда есть task<int>
, но, как вы можете видеть, это не обязательно так, если вы немедленно позвоните then
в теме.
Во-вторых, имейте в виду, что ваша функция возвращает task
, а не сам поток.
Обычно у вас будет последний then
в вашей цепочке верните вам задачу, которую вы вернете из своей функции, и вместо того, чтобы сохранять задачу в локальной переменной, вы просто возвращаете ее. Например:
task<const char*>
get_foo()
{
return task<int>([] { return 0; })
.then([](int i) { return 3.14; })
.then([](double d) { return "foo"; });
}
Опять же, это выглядит немного странно, потому что быстрый взгляд заставляет вас думать, что вы возвращаете task<int>
, Это хорошо обрабатывается с помощью create_task
вместо того, чтобы вызывать конструктор задачи явно. Это освобождает вас от необходимости явно указывать тип задачи. Кроме того, он легко меняется на create_async
если вы вместо этого хотите вернуть IAsyncInfo
производное.
Я совсем не знаком с BitmapEncoder
, но вот измененная версия вашего кода, которая может сработать:
Concurrency::task<IRandomAccessStream^> GetImageStream()
{
auto stream = ref new InMemoryRandomAccessStream();
return create_task(BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, stream))
.then([this, width, height, imageBytes](BitmapEncoder^ encoder)
{
// are width, height, and imageBytes member variables?
// if so, you should only need to capture them OR "this", not both
encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 96.0, 96.0, imageBytes);
return encoder->FlushAsync();
}).then([stream]()
{
// this should work fine since "stream" is a ref-counted variable
// this lambda will keep a reference alive until it uses it
return stream;
});
}
Единственное реальное изменение использует create_task
и сразу же возвращает свой результат.
Я все еще изучаю PPL самостоятельно, но одна вещь, которую я узнал, которая до сих пор держится, это то, что вы должны в значительной степени всегда делать что-то с любой задачей, которую вы создаете. Обычная вещь, чтобы сделать, это использовать then
чтобы превратить его в новую / другую задачу, но вам все равно нужно что-то сделать с задачей, возвращенной последней then
, Часто вы просто возвращаете его, как указано выше. Иногда вы добавляете его в контейнер задач, которые затем группируются вместе с when_all
или же when_any
, Иногда вы просто позвоните get()
на нем самим ждать его результата. Но дело в том, что если вы создаете задачу и ничего с ней не делаете, то, вероятно, что-то не так.
Также, если кому-то все равно, вот как реализовать упомянутый выше GetImageStreamRef в версии C ++:
task<RandomAccessStreamReference^> GetImageStreamRef()
{
return GetImageStream().then([](IRandomAccessStream^ pStream)
{
return RandomAccessStreamReference::CreateFromStream(pStream);
});
}
Кроме того, убедитесь, что НИКОГДА не используете .get () для любой из этих задач, в противном случае вы получите исключение, говорящее «Недопустимо ожидать выполнения задачи в STA среды выполнения Windows». Правильный способ получить значение этой ссылки на поток изображения, например, на DataRequestHandler, который устанавливает данные растрового изображения на DataRequest:
void OnBitmapRequested(DataProviderRequest^ request)
{
auto Deferral = request->GetDeferral();
GetImageStreamRef().then([Deferral, request](RandomAccessStreamReference^ pStreamRef)
{
try
{
request->SetData(pStreamRef);
Deferral->Complete();
}
catch( std::exception ex )
{
Deferral->Complete();
throw ex; //Propagate the exception up again
}
});
}