Reputation: 1220
I'm attempting to create a wrapper allowing System::Task objects to be created in a one liner. So far I have the following working -
ref class TaskRunner abstract sealed
{
public:
generic<typename TArg0>
static Task^ Create(Action<TArg0>^ f, TArg0 arg0) {
return Task::Run((gcnew BindAction1<TArg0>(f, arg0))->binder);
}
private:
generic<typename TArg0>
ref class BindAction1
{
initonly TArg0 _arg0;
Action<TArg0>^ const f;
void _() { return f(_arg0); }
public:
initonly Action^ binder;
BindAction1(Action<TArg0>^ f, TArg0 arg0) : f(f), _arg0(arg0) {
binder = gcnew Action(this, &BindAction1::_);
}
};
}
Which can be called via -
auto task = TaskRunner::Create(gcnew Action<TArg0>(this, &Class::Function), arg0);
However i'd like to remove the need to create the action manually. I.e. modify Create so the call looks like-
auto task = TaskRunner::Create(this, &Class::Function, arg0);
and call gcnew Action(obj, func) inside of Create. I don't know if this is possible. If so how would I declare the arguments for create?
Effectively they need to be the same as the Action delegate. Looking at the system library Action seems to take a System::Object and System::IntPtr but i can't get it to work using those as arguments.
*Update - Thanks IllidanS4 for the complete answer and your help in getting this working. For anyone attempting to use the code in his answer there appears to be a compiler bug in Visual Studio 2015 which requires the delegate_type to be overridden for each argument chain length.
Upvotes: 1
Views: 453
Reputation: 13187
From ECMA-372:
A program that attempts to take the address of a member function of a non-native class in any context other than in the creation of a delegate, is ill-formed. There is no pointer-to-member representation for members of non-native classes.
This, unfortunately, means, that &Class::Function
is invalid. In my opinion, this restriction is useless, because the ldftn
instruction in CIL is precisely what this is nontheless compiled to in a delegate construction. A "pointer to managed function" type is missing in C++/CLI sadly, although it clearly exists in CIL.
So no, a method must be passed around contained in a delegate. However, if you are open to macros, you can use something like this:
#define CreateTask(obj, func, arg) TaskRunner::Create(gcnew System::Action<decltype(arg)>(obj, func), arg)
Edit: It's a bit tricky to make this also work for the Func case, but it works too with some templating:
template <class Ret, class... Args>
struct delegate_type
{
using type = System::Func<Args..., Ret>;
};
template <class... Args>
struct delegate_type<void, Args...>
{
using type = System::Action<Args...>;
};
#define CreateTask(obj, func, arg) TaskRunner::Create(gcnew delegate_type<decltype(obj->func(arg)), decltype(arg)>::type(obj, &func), arg)
Just don't use it with &Class::Function
, pass Class::Function
instead.
Upvotes: 2