C ++ / CLIでタスクに引数を渡しますか?

2012-10-25 c++-cli arguments action task

Visual Studio 2012のC#用のこのコードがあります。

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

これにより、 SwitchLaserメソッド(クラスMyClass public非静的メンバー)が、引数boolがオンのタスクとして実行されます。

マネージドC ++ / CLIでも同様のことをしたいと思います。しかし、1つのパラメーターを使用してメンバーメソッドを実行するタスクを実行する方法を見つけることができません。

現在の解決策は次のとおりです:

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

SwitchLaserHelper関数の実装:

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

C#のような解決策があり、ヘルパー関数とメンバーを作成しないようにする必要があります(これはスレッドセーフではありません)。

Answers

これを行う方法はまだありません。

C#では、クロージャーがあります。 C ++ / CLIコンパイラーが作成されたとき、C ++のクロージャーの標準化された構文はまだ議論されていました。ありがたいことに、マイクロソフトはさらに別の独自の構文を導入する代わりに、標準のラムダ構文を使用することを選択しました。残念ながら、この機能はまだ利用できません。その場合、次のようになります。

gcnew Action([this, on](){ SwitchLaser(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構文は単純にそのヘルパークラスを作成します(現在、ネイティブラムダでは機能しますが、マネージラムダではまだ機能しません)。

これが私が今日の午後に書いた一般的なコードで、これは役立つかもしれません(ただし、この質問と完全に一致するわけではありません)。多分これはこの質問に出くわす次の人を助けるでしょう。

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

値を返さないメソッドを実行するタスクにパラメーターを提供したい場合も同様の問題がありました( void返しvoid )。そのFunc<T, TResult>ため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で使用するために見つけることができる最高のソリューションです。

動作する答えは次のとおりです。テストしてみました。アクションsampleFunctionに引数(int)を渡します。

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

Related