2013-05-23 3 views
0

Я всего нуб, когда дело доходит до использования задач PPL в среде C++, так что я с трудом, чтобы выяснить, что будет синтаксис C++ следующего C# код:C++ Функциональная подпись, возвращающая задачу PPL?

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.

Я даже не уверен, что я взял правильный путь в это ...

Спасибо!

+0

Вы компиляции выше код как код C++/CLI или обычный C++ код? Он не будет работать как обычный код на C++, поскольку сигнатуры каретки для типов ссылок, собранных для мусора, являются расширением C++/CLI. –

+0

@TimoGeusch: это не C++/CLI, это C++/CX: http://programmers.stackexchange.com/questions/162168/what-are-c-cx-and-c-cli-and-how-do-they -relate-to-c-and-winrt – Matt

+0

@TimoGeusch Да Я компилирую как C++/CX. Благодарю. – Deathicon

ответ

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 на нем.

Во-вторых, имейте в виду, что ваша функция возвращает 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() на себя, чтобы ждать своего результата.Но дело в том, что если вы создаете задачу и ничего не делаете с ней, то, вероятно, что-то не так.

+0

Спасибо, ваш ответ был очень проницательным! Фактически, я смог сделать все это сейчас! :) – Deathicon

+0

В последней детали функция GetImageStream должна возвращать InMemoryRandomAccessStream^вместо IRandomAccessStream ^, иначе вы получите ошибку компиляции. – Deathicon

+0

Отлично, я рад, что у вас это работает! Один незначительный момент в вашем ответе (который я, похоже, не могу комментировать напрямую): вы получаете только исключение, которое вы указываете из '.get()', когда вы вызываете его ** в потоке пользовательского интерфейса **. Это способ WinRT не позволять вам останавливать пользовательский интерфейс, чтобы ждать долговременной работы. Однако, если вы находитесь в фоновом потоке, вы можете вызвать '.get()' по мере необходимости. – Jayonas

0

Кроме того, если кто-то заботится, вот как реализовать GetImageStreamRef упоминалось выше, в C++ версии:

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

Кроме того, убедитесь, что НИКОГДА не используйте .get() на любой из этих задач, в противном случае вы будете получите исключение, сказанное «Недопустимо ждать задачи в Windows Runtime 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 
     } 
    }); 
}