Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass an argument to task in C++/CLI?

I have this code for the C# in Visual Studio 2012.

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

This will execute SwitchLaser method (public nonstatic member of a class MyClass) as a task with argument bool on.

I would like to do something similar in managed C++/CLI. But I am not able to find out any way how to run a task, which will execute a member method taking one parameter.

Current solution is like this:

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

Implementation of SwitchLaserHelper function:

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

There must be some solution like in C# and not to create helper functions and members (this is not threadsafe).

like image 241
Bezousek Avatar asked Oct 25 '12 15:10

Bezousek


3 Answers

There isn't yet any way to do this.

In C# you have a closure. When your C++/CLI compiler was written, the standardized syntax for closures in C++ was still being discussed. Thankfully, Microsoft chose to wait and use the standard lambda syntax instead of introducing yet another unique syntax. Unfortunately, it means the feature isn't yet available. When it is, it will look something like:

gcnew Action([this, on](){ SwitchLaser(on) });

The current threadsafe solution is to do what the C# compiler does -- put the helper function and data members not into the current class, but into a nested subtype. Of course you'll need to save the this pointer in addition to your local variable.

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

The C++ lamdba syntax will simply create that helper class for you (currently it works for native lambdas, but not yet for managed ones).

like image 181
Ben Voigt Avatar answered Oct 23 '22 23:10

Ben Voigt


Here's generic code I wrote this afternoon which might help (although it's not an exact match for this question). Maybe this will help the next person who stumbles onto this question.

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

Usage is

const auto f = gcnew Func<T, TResult>(this, &MyClass::MyMethod);
return Task::Run(Binder::Create(f, arg));
like image 5
Ðаn Avatar answered Oct 23 '22 21:10

Ðаn


Here's the working answer.. Have tested it.. Passing an argument (int) to the action sampleFunction.

#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;
}
like image 3
Viswanadh Avatar answered Oct 23 '22 21:10

Viswanadh