С ++ Функция подписи, которая возвращает задачу PPL?

Я абсолютный нуб, когда дело доходит до использования задач 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.

Я даже не уверен, что выбрал правильный путь в этом …

Спасибо!

0

Решение

Ты очень близко. Ключевым моментом, который вы можете упустить, является то, что 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() на нем самим ждать его результата. Но дело в том, что если вы создаете задачу и ничего с ней не делаете, то, вероятно, что-то не так.

2

Другие решения

Также, если кому-то все равно, вот как реализовать упомянутый выше 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
}
});
}
0