Joe
Joe

Reputation: 6816

Compilation error creating System::Threading::Tasks in C++/CLI. Unable to spot the error

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

Answers (1)

Xeyone T
Xeyone T

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

Related Questions