返回PPL任务的c++函数签名

C++ Function signature that returns a PPL task?

本文关键字:函数 c++ PPL 任务 返回      更新时间:2023-10-16

当涉及到在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的反向微软示例代码。到目前为止,我能得到的最好的是:

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而不是显式调用任务构造函数,可以很好地处理这个问题。它使您完全不必显式地指定任务的类型。此外,如果您想返回IAsyncInfo的衍生物,则很容易将其更改为create_async

我对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()来等待其结果。但关键是,如果你创建了一个任务,但没有对它做任何事情,那么很可能是哪里出了问题。

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

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
        }
    });
}