Пропустить аргумент для задачи в С++/CLI?

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

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

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

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

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

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

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

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

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

Ответ 1

Пока этого не сделать.

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

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

Текущее потоковое решение - делать то, что делает компилятор С#, - помещать вспомогательную функцию и элементы данных в текущий класс, но во вложенный подтип. Конечно, вам нужно сохранить указатель 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));
}

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

Ответ 2

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

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

Ответ 3

У меня была аналогичная проблема, когда я хотел предоставить параметр для задачи, выполняющей метод, который не возвращает значение (retuns 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()));

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