IronPython: How to call a function that expects an

2019-07-21 03:14发布

问题:

I have come across a problem with IronPython that I can't solve. I need to call a function that takes a parameter of type array to value-type. The function-signature (in C++/CLI notation) is this:

static int PLGServiceInterface::PLGService::MapLowSpeedRealtimeNames(cli::array<System::String ^> ^ SignalNames, int timeout_ms, cli::array<PLGServiceInterface::LVCluster_1> ^% Channels, System::String ^% ReportText)

When I call the function from IronPython

import clr
clr.AddReferenceToFileAndPath('PLGServiceAPI.dll')

from System import String, Array
from PLGServiceInterface import PLGService, LVCluster_1

outtext = clr.Reference[String]()
outdata = clr.Reference[Array[LVCluster_1]]()
PLGService.MapLowSpeedRealtimeNames(('hello', 'world'), 300, outdata, outtext)

I get the following error

Traceback (most recent call last):
  File "test2.py", line 9, in <module>
TypeError: expected StrongBox[Array[LVCluster_1]], got StrongBox[Array[LVCluster_1]]

The error message is not very helpful but I assume the problem is that "outdata" is an array containing value types instead of reference types. Apparently IronPython doesn't know how to do the boxing in that case.

With C++/CLI I can use the function just fine:

using namespace System;
using PLGServiceInterface::LVCluster_1;
using PLGServiceInterface::PLGService;

int main(array<System::String ^> ^args)
{
    array<LVCluster_1> ^outdata;
    array<String^> ^names = gcnew array<String^>{"one", "two"};
    String ^o;

    PLGService::MapLowSpeedRealtimeNames(names, 300, outdata, o);

    Console::WriteLine(o);

    Console::Read();

    return 0;
}

I assume if the function would instead expect an array of references

array<LVCluster_1 ^> ^outdata

I could call it with IronPython.

Is there any way to make this work with IronPython? By the way, the assembly was created with LabView and LVCluster_1 is a LabView-Cluster (structure).

Edit: The Intermediate Language signature of MapLowSpeedRealtimeNames is this:

.method public hidebysig static int32  MapLowSpeedRealtimeNames(string[...] SignalNames,
                                                                int32 timeout_ms,
                                                                [out] valuetype PLGServiceInterface.LVCluster_1[...]& Channels,
                                                                [out] string& ReportText) cil managed

Does anybody know what the meaning of the 3 dots in the array brackets is? When I compile a function that takes an array in C++/CLI, I only get the opening and closing brackets without the dots in between.

These dots seem suspicious to me because I also get the TypeError when calling a method that takes an out-parameter of type array of double(float64):

.method public hidebysig static int32  ReadVariables(string[...] SignalNames,
                                                     int32 timeout_ms,
                                                     [out] string& ReportText,
                                                     [out] float64[...]& Data) cil managed

Generates the error

TypeError: expected StrongBox[Array[float]], got StrongBox[Array[float]]

回答1:

Have you tried using Array.CreateInstance(LVCluster_1, length) to create the array? See example below:

outdata = Array.CreateInstance(LVCluster_1, 2)

The code above works for me, but my c++/cli function signature don't use the tracking reference (%). So if I were to re-write your function signature it would look like:

static int PLGServiceInterface::PLGService::MapLowSpeedRealtimeNames(cli::array<System::String ^> ^ SignalNames, int timeout_ms, cli::array<PLGServiceInterface::LVCluster_1> ^ Channels, System::String ^ ReportText)