Reputation: 6816
I'm trying to make a C++/CLI wrapper layer (that calls into an unmanaged C++ DLL) expose functions that return Task<T>
to my C# layer. The unmanaged functions are expensive which is why I wanted asynchronous versions.
But I can't get it to build.
My approach is to use TaskFactory::StartNew in C++/CLI supplying a System::Func that takes one argument and returns one value. The prototype of that, in MSDN is as follows:
StartNew<TResult>(Func<Object,TResult>, Object)
(It seems I must use this if I want my created task to pass arguments into my expensive, synchronous function. I make "Object" contain all the args I need, wrapped up into some internal class. My code can unpack it and use them.)
Below is a simple C++/CLI example I tried to whip up to show the problem. And below that are the errors I get. I have clearly identified the failing line with "THIS LINE FAILS". There's also another line that shows with red Squiggly in Visual Studio but doesn't cause a compiler error.
The code (This is all in VS 2017)
using namespace System;
using namespace System::Threading;
using namespace System::Threading::Tasks;
namespace CppCliLayer
{
// Arguments class. Because TaskFactory.StartNew only appears to allow us one
// argument to our task function, at best (a System::Object). This packs up the
// multiple arguments we'll need
public ref class Arguments
{
String^ string;
int intVal;
public:
Arguments(String^ s, int n) { string = s; intVal = n; }
property String^ Text { String^ get() { return string; } }
property int Token { int get() { return intVal; } }
};
// "Expensive": Want to return an instance of this from a long running task..
public ref class Expensive
{
public:
static Expensive^ CreateExpensively(Arguments^)
{
// Do slow stuff with arguments, then create.
// <Imagine slow synchronous code here....>
return gcnew Expensive();
}
};
// Function that takes a long time to create an instance of Expensive
Task<Expensive^>^ ExpensivelyCreateAsync(String^ s, int n)
{
auto func = gcnew Func<Arguments^, Expensive^>(&Expensive::CreateExpensively); // *** VS UNDERLINES THIS WITH A SQUIGGLY (see below for more)
auto args = gcnew Arguments(s, n);
return TaskFactory::StartNew<Expensive^>(func, args); // *** THIS LINE FAILS
}
}
And the errors
1>------ Build started: Project: CppCliLayer, Configuration: Debug x64 ------
1>testcli.cpp
1>d:\dev\testcli.cpp(130): error C2665: 'System::Threading::Tasks::TaskFactory::StartNew': none of the 3 overloads could convert all the argument types
1>d:\dev\testcli.cpp(130): note: could be 'System::Threading::Tasks::Task<CppCliLayer::Expensive ^> ^System::Threading::Tasks::TaskFactory::StartNew<CppCliLayer::Expensive^>(System::Func<System::Object ^,CppCliLayer::Expensive ^> ^,System::Object ^)'
1>d:\dev\testcli.cpp(130): note: or 'System::Threading::Tasks::Task<CppCliLayer::Expensive ^> ^System::Threading::Tasks::TaskFactory::StartNew<CppCliLayer::Expensive^>(System::Func<CppCliLayer::Expensive ^> ^,System::Threading::Tasks::TaskCreationOptions)'
1>d:\dev\testcli.cpp(130): note: or 'System::Threading::Tasks::Task<CppCliLayer::Expensive ^> ^System::Threading::Tasks::TaskFactory::StartNew<CppCliLayer::Expensive^>(System::Func<CppCliLayer::Expensive ^> ^,System::Threading::CancellationToken)'
1>d:\dev\testcli.cpp(130): note: while trying to match the argument list '(System::Func<CppCliLayer::Arguments ^,CppCliLayer::Expensive ^> ^, CppCliLayer::Arguments ^)'
1>Done building project "testcli_v15.vcxproj" -- FAILED.
StopOnFirstBuildError: Build cancelled because project "corecli_v15" failed to build.
Build has been canceled.
I should mention that my C++/CLI dll is targeting.NET Framework 4.6.2 I should also mention that the compiler underlines this line with a red squiggly
auto func = gcnew Func<Arguments^, Expensive^>(&Expensive::CreateExpensively);
and the message it gives me when I hover over it is "expected a type specifier". But it does not generate a compilation error.
Can anyone see what I am doing wrong here?
Upvotes: 2
Views: 874
Reputation: 1
The async function CreateExpensively
function can only take in Object
as parameter, it cannot take in other objects like Arguments
. You can cast Arguments
to Object
in CreateExpensively().
Instead of using TaskFactory::StartNew
, create a new instance of Task
.
The red squiggly I believe is an intellisense parser bug, I see it in my VS2017 too.
The working code is as below:
// "Expensive": Want to return an instance of this from a long running task..
public ref class Expensive
{
public:
static Expensive^ CreateExpensively(Object^ argObj)
{
Arguments^ args = (Arguments^) argObj; // you can pass it Arguments here
// Do slow stuff with arguments, then create.
// <Imagine slow synchronous code here....>
return gcnew Expensive();
}
Task<Expensive^>^ ExpensivelyCreateAsync(String^ s, int n)
{
auto func = gcnew Func<Object^, Expensive^>(&Expensive::CreateExpensively);
Arguments^ args = gcnew Arguments(s, n);
// Use Task instead of StartNew
Task<Expensive^>^ asyncTask = gcnew Task<Expensive^>(func, args);
asyncTask->Start();
return asyncTask;
}
};
Upvotes: -1