问题 在C ++ / CLI中将参数传递给任务?


我在Visual Studio 2012中有C#的代码。

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#中的解决方案而不是创建辅助函数和成员(这不是线程安全的)。


12109
2017-10-25 15:15


起源



答案:


目前还没有办法做到这一点。

在C#中你有一个闭包。编写C ++ / CLI编译器时,仍在讨论C ++中闭包的标准化语法。值得庆幸的是,Microsoft选择等待并使用标准的lambda语法,而不是引入另一种独特的语法。不幸的是,这意味着该功能尚不可用。如果是,它将看起来像:

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语法将为您创建该助手类(目前它适用于本机lambda,但尚未用于托管类)。


11
2017-10-25 16:27



感谢你的回答。很明显,托管代码不支持lambda。我仍然想避免创建辅助类。我在文档中找到的是托管C ++的Action <T>类。这应该能够使用一个参数获取一个函数但我无法使用它,特别是使用Task :: Run方法(或以某种方式将它与Task类一起使用)。有没有辅助类或辅助方法的解决方案? - Bezousek
@Bezousek: Task::Run 需要一个 Action 代表。不,一个 Action<T> 是不一样的类型。闭包是使用帮助程序类实现的。这就是C#编译器根据您的问题中的示例代码创建的内容,如果添加了托管的lambda功能,这就是C ++ / CLI编译器有朝一日会做的事情。 - Ben Voigt


答案:


目前还没有办法做到这一点。

在C#中你有一个闭包。编写C ++ / CLI编译器时,仍在讨论C ++中闭包的标准化语法。值得庆幸的是,Microsoft选择等待并使用标准的lambda语法,而不是引入另一种独特的语法。不幸的是,这意味着该功能尚不可用。如果是,它将看起来像:

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语法将为您创建该助手类(目前它适用于本机lambda,但尚未用于托管类)。


11
2017-10-25 16:27



感谢你的回答。很明显,托管代码不支持lambda。我仍然想避免创建辅助类。我在文档中找到的是托管C ++的Action <T>类。这应该能够使用一个参数获取一个函数但我无法使用它,特别是使用Task :: Run方法(或以某种方式将它与Task类一起使用)。有没有辅助类或辅助方法的解决方案? - Bezousek
@Bezousek: Task::Run 需要一个 Action 代表。不,一个 Action<T> 是不一样的类型。闭包是使用帮助程序类实现的。这就是C#编译器根据您的问题中的示例代码创建的内容,如果添加了托管的lambda功能,这就是C ++ / CLI编译器有朝一日会做的事情。 - Ben Voigt


这是我今天下午写的通用代码,可能有所帮助(虽然它不是这个问题的完全匹配)。也许这会帮助下一个偶然发现这个问题的人。

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
2018-02-17 01:13





当我想为执行不返回值的方法的任务提供参数时,我遇到了类似的问题(返回 void)。因此 Func<T, TResult> 不是我可以使用的选项。有关详细信息,请查看页面 使用带有新Func的void返回类型

所以我最终得到了一个解决方案,我创建了一个帮助类

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中使用的最好的解决方案,它不支持lambda表达式。


0
2017-07-06 07:06