返回PPL任务的C ++函数签名?

时间:2013-05-23 17:46:24

标签: c++ task ppl clx

在C ++环境中使用PPL任务时,我是一个完全的菜鸟,所以我很难弄清楚以下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 reversi Microsoft sample code。到目前为止我能得到的最好的是:

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问题。

我甚至不确定我是否采取了正确的方法......

谢谢!

2 个答案:

答案 0 :(得分:2)

你真的很亲近。您可能缺少的一个关键点是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就不一定如此。< / p>

其次,请记住,您的函数返回的是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_allwhen_any组合在一起。有时您只需自己调用get()即可等待其结果。但重点是,如果你创建一个任务并且不对它做任何事情,那么可能有些不对劲。

答案 1 :(得分:0)

如果有人关心,这里是如何在C ++版本中实现上面提到的GetImageStreamRef:

task<RandomAccessStreamReference^> GetImageStreamRef()
{
    return GetImageStream().then([](IRandomAccessStream^ pStream)
    {
        return RandomAccessStreamReference::CreateFromStream(pStream);
    });
}

此外,请确保永远不要在任何这些任务上使用.get(),否则会抛出一个异常,说“非法等待Windows运行时STA中的任务”。获取此图像流引用值的正确方法是,例如在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
        }
    });
}