Передать аргумент в задачу в C ++ / CLI?

У меня есть этот код для C # в Visual Studio 2012.

public Task SwitchLaserAsync(bool on)
{
   return Task.Run(new Action(() => SwitchLaser(on)));
}

Это выполнитSwitchLaser метод (открытый нестатический член классаMyClass) как задание с аргументом bool on.

Я хотел бы сделать нечто подобное в управляемой C ++ / CLI. Но я не могу узнать, как запустить задачу, которая будет выполнять метод-член, принимающий один параметр.

Текущее решение выглядит так:

Task^ MyClass::SwitchLaserAsync( bool on )
{
    laserOn = on;   //member bool 
    return Task::Run(gcnew Action(this, &MyClass::SwitchLaserHelper));
}

РеализацияSwitchLaserHelper функция:

void MyClass::SwitchLaserHelper()
{
     SwitchLaser(laserOn);
}

Должно быть какое-то решение, как в C #, и не создавать вспомогательные функции и члены (это не потокобезопасно).

Ответы на вопрос(4)

который я написал сегодня днем, который может помочь (хотя это не точное соответствие для этого вопроса). Возможно, это поможет следующему человеку, который наткнется на этот вопрос.

generic<typename T, typename TResult>
ref class Bind1
{
    initonly T arg;
    Func<T, TResult>^ const f;
    TResult _() { return f(arg); }

public:
    initonly Func<TResult>^ binder;
    Bind1(Func<T, TResult>^ f, T arg) : f(f), arg(arg) {
        binder = gcnew Func<TResult>(this, &Bind1::_);
    }
};

ref class Binder abstract sealed // static
{
public:
    generic<typename T, typename TResult>
    static Func<TResult>^ Create(Func<T, TResult>^ f, T arg) {
        return (gcnew Bind1<T, TResult>(f, arg))->binder;
    }
};

Использование

const auto f = gcnew Func<T, TResult>(this, &MyClass::MyMethod);
return Task::Run(Binder::Create(f, arg));

unction действия.

#include "stdafx.h"
#include "CLRSamples.h"

using namespace System;
using namespace System::Threading;
using namespace System::Threading::Tasks;
using namespace System::Collections;
using namespace System::Collections::Generic;

void CLRSamples::sampleFunction(Object^ number)
{
    Console::WriteLine(number->ToString());
    Thread::Sleep((int)number * 100);
}

void CLRSamples::testTasks()
{
    List<Task^>^ tasks = gcnew List<Task^>();

    for (int i = 0; i < 10; i++)
    {
        tasks->Add(Task::Factory->StartNew((Action<Object^>^)(gcnew Action<Object^>(this, &CLRSamples::sampleFunction)), i));
    }

    Task::WaitAll(tasks->ToArray());

    Console::WriteLine("Completed...");
}

int main(array<System::String ^> ^args)
{
    CLRSamples^ samples = gcnew CLRSamples();
    samples->testTasks();

    Console::Read();
    return 0;
}

В C # у вас есть замыкание. Когда был написан ваш компилятор C ++ / CLI, стандартизированный синтаксис замыканий в C ++ все еще обсуждался. К счастью, Microsoft решила подождать и использовать стандартный лямбда-синтаксис вместо введения еще одного уникального синтаксиса. К сожалению, это означает, что функция еще не доступна. Когда это так, это будет выглядеть примерно так:

gcnew Action([on](){ SwitchLaserHelper(on) });

Текущее поточно-безопасное решение состоит в том, чтобы сделать то, что делает компилятор C # - поместить вспомогательную функцию и члены данных не в текущий класс, а во вложенный подтип. Конечно, вам нужно сохранитьthis указатель в дополнение к вашей локальной переменной.

ref class MyClass::SwitchLaserHelper
{
    bool laserOn;
    MyClass^ owner;

public:
    SwitchLaserHelper(MyClass^ realThis, bool on) : owner(realThis), laserOn(on) {}
    void DoIt() { owner->SwitchLaser(laserOn); }
};

Task^ MyClass::SwitchLaserAsync( bool on )
{
    return Task::Run(gcnew Action(gcnew SwitchLaserHelper(this, on), &MyClass::SwitchLaserHelper::DoIt));
}

Синтаксис C ++ lamdba просто создаст этот вспомогательный класс для вас (в настоящее время он работает для нативных лямбд, но еще не для управляемых).

 Bezousek26 окт. 2012 г., 09:11
Спасибо за ответ. Понятно, что лямбда не поддерживается в управляемом коде. Еще я бы хотел избежать создания вспомогательных классов. В документации я обнаружил класс Action <T> для управляемого C ++. Это должно быть в состоянии получить функцию с одним аргументом, но я не смог ее использовать, особенно с методом Task :: Run (или каким-то образом использовать его с классом Task). Есть ли решение без вспомогательного класса или вспомогательных методов?
 Ben Voigt26 окт. 2012 г., 14:27
@Bezousek:Task::Run требуетAction делегировать. НетAction<T> не тот же тип. Замыкания реализованы с помощью вспомогательного класса. Это то, что компилятор C # создает из вашего примера кода в вашем вопросе, и именно это когда-нибудь сделает компилятор C ++ / CLI, если будут добавлены управляемые функции лямбды.

когда я хотел предоставить параметр для задачи, выполняющей метод, который не возвращает значение (повторыvoid). Потому чтоFunc<T, TResult> не был вариант, который я мог бы использовать. Для получения дополнительной информации, пожалуйста, проверьте страницуИспользование void возвращаемых типов с новым Func.

Таким образом, я получил решение, в котором я создал вспомогательный класс

template <typename T>
ref class ActionArguments
{
public:
    ActionArguments(Action<T>^ func, T args) : m_func(func), m_args(args) {};
    void operator()() { m_func(m_args); };

private:
    Action<T>^ m_func;
    T m_args;
};

который используетAction<T> делегат для инкапсуляции метода, который имеет единственный параметр и не возвращает значение.

Я бы тогда использовал этот вспомогательный класс следующим образом

ref class DisplayActivationController
{
public:
    DisplayActivationController();

    void StatusChanged(EventArgs^ args) { };
}


Action<EventArgs^>^ action =
    gcnew Action<EventArgs^>(this, &DisplayActivationController::StatusChanged);
ActionArguments<EventArgs^>^ action_args =
    gcnew ActionArguments<EventArgs^>(action, args);
Threading::Tasks::Task::Factory->
    StartNew(gcnew Action(action_args, &ActionArguments<EventArgs^>::operator()));

Подход с использованием вспомогательного класса, вероятно, не самое элегантное решение, но это лучшее, что я могу найти для использования в C ++ / CLI, который не поддерживает лямбда-выражения.

Ваш ответ на вопрос