2015-03-27 1 views
2

J'ai rencontré un problème avec IronPython que je ne peux pas résoudre. J'ai besoin d'appeler une fonction qui prend un paramètre de type array à value-type. La fonction de signature (en notation C++/CLI) est la suivante:IronPython: Comment appeler une fonction qui attend un tableau de types-valeur?

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

Quand j'appelle la fonction de 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) 

J'obtiens l'erreur suivante

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

Le message d'erreur n'est pas très utile mais je suppose que le problème est que "outdata" est un tableau contenant des types de valeurs au lieu de types de références. Apparemment, IronPython ne sait pas comment faire la boxe dans ce cas.

Avec C++/CLI je peux utiliser la fonction très bien:

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

Je suppose que si la fonction serait plutôt attendre un tableau de références

array<LVCluster_1 ^> ^outdata 

Je pourrais l'appeler avec IronPython.

Est-il possible de faire fonctionner cela avec IronPython? A propos, l'assemblage a été créé avec LabView et LVCluster_1 est un LabView-Cluster (structure).

Edit: La signature de langue intermédiaire MapLowSpeedRealtimeNames est la suivante:

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

Quelqu'un sait-il ce que le sens des 3 points dans les tranches de tableau est? Lorsque je compile une fonction qui prend un tableau en C++/CLI, je n'obtiens que les parenthèses d'ouverture et de fermeture sans les points intermédiaires.

Ces points semblent suspects pour moi parce que je reçois aussi TypeError lorsque vous appelez une méthode qui prend un hors paramètre de tableau de type double (float64):

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

génère l'erreur

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

Répondre

0

Avez-vous essayé d'utiliser Array.CreateInstance (LVCluster_1, length) pour créer le tableau? Voir exemple ci-dessous:

outdata = Array.CreateInstance(LVCluster_1, 2) 

Le code ci-dessus fonctionne pour moi, mais mon C++/signature de la fonction cli ne pas utiliser la référence de suivi (%). Donc, si je devais réécrire la signature de votre fonction, cela ressemblerait à:

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

Cela pourrait fonctionner si "outdata" n'était pas un paramètre externe. Mais dans mon cas c'est un out-paramètre et par conséquent cela n'a pas de sens de créer un tableau avant d'appeler la fonction. Je dois utiliser clr.Reference mais je ne sais pas comment je peux spécifier que le type entre crochets est un type par valeur. – Krid

+0

Est-ce que "outdata" doit être un paramètre externe? Normalement, seuls les types primitifs sont transmis par valeur. Les tableaux et les objets complexes sont généralement transmis par référence, car cela coûte moins cher. L'idée ici est de créer un tableau dans le code managé, de transmettre la référence au code non managé et de copier les données du code non managé vers le code managé. –

+0

Dans le code ci-dessus, le tableau de LVCluster_1 n'est pas copié par valeur. Une référence au outdata-handle est transmise à la fonction (via le paramètre "Channels"). La fonction va alors modifier le handle pour qu'il pointe vers un objet-tableau qu'il a créé en interne. Votre idée pourrait quand même fonctionner car je sais que le tableau outdata doit avoir le même nombre d'éléments que le tableau de chaînes passé en premier paramètre. Mais que feriez-vous si vous ne connaissiez pas le nombre d'éléments du tableau de sortie? Dans ce cas, vous ne pouvez pas pré-allouer la mémoire dans la fonction appelante. – Krid