2011-11-08 3 views
1

Enquête précédente dans Can I make a table of String + lambdas that have the same signature? m'a montré que je peux en fait avoir une table de chaînes + lambdas dans VS2010.Travailler autour de VS2010 bug dans la table de lambdas?

Les choses allaient bien alors que les lambdas étaient vides. Mais ayant essayé de les changer en type de retour booléen, le compilateur semble se tromper, ou il y a une sorte d'erreur de corruption de mémoire ... quelque chose ne va pas en C++ ...

Le scénario suivant illustre le scénario suivant:

// fun: use a table of lambdas to define what to do in order to update each field 
typedef std::function<bool (CDynamicMenuItem *, ITEM *)> LambdaType; 
struct UpdateField { 
    const TCHAR * label; 
    LambdaType  lambda; // this version allows us to use captures in our lambdas, whereas the following doesn't 
    //void (*lambda)(CDynamicMenuItem *, ITEM *); // this would work in VS11, but the conversion of lambda to function pointer was defined after 2010's release! 
}; 
UpdateField MenuFields[] = { 
    { "Identity", [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { return pMenuItem->SetText(FString("%s/%.1f", pNearestItem->thissec->name, pNearestItem->number/10.0)), true; } }, 
    { "X1",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetX1(pNearestItem); return (v != v) ? false : pMenuItem->SetValue(v), true; } }, 
    { "Y1",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetY1(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "X2",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetX2(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "Y2",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetY2(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "Xd",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetXd(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "Yd",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetYd(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "Angle",  [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetAngle(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
    { "Length",  [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetLength(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } }, 
}; 

for (UpdateField * it = &MenuFields[0], * end = (MenuFields + countof(MenuFields)); it != end; ++it) 
{ 
    CDynamicMenuItem * pMenuItem = pMenu->FindItem(it->label); 
    if (pMenuItem) 
    { 
     if (!m_pNearestItem || !it->lambda(pMenuItem, m_pNearestItem)) 
      pMenuItem->SetText(""); 
    } 
} 

ci-dessus a fonctionné à merveille lorsque le type de retour du lambda est vide (ie -> bool est omis, et les différents organismes lambda sont modifiés pour ne pas retourner quoi que ce soit, etc.).

Cependant, il est utile pour moi de leur renvoyer un bool qui indique si le lambda était capable de traiter les données pour ce champ et, dans le cas contraire, de demander à l'appelant de gérer ce champ.

Pour être certain, le code compile & s'exécute ... jusqu'à ce qu'il frappe ce code & CRASHES. En regardant "MenuFields []" dans le débogueur, la plupart des adresses MenuField [x] .label sont affichées (parfois l'une d'entre elles est correcte, mais je n'ai pas compris le motif). Je pensais que peut-être le compilateur glitching sur la syntaxe de la lambda intégrée dans la liste d'initialisation statique, mais je ne suis pas sûr de ce que je peux faire à ce sujet?

J'ai essayé cette variation:

{ "Identity", LambdaType([] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { return pMenuItem->SetText(FString("%s/%.1f", pNearestItem->thissec->name, pNearestItem->number/10.0)), true; }) }, 

Le compilateur aime ça aussi bien, mais il en résulte dans les mêmes données de table corrompues. De même, mettre des parenthèses autour du lambda entier est correct par le compilateur, et également corrompu au moment de l'exécution.

Alors, quelques questions:

  1. Voyez-vous quelque chose que je négligé? Pouvez-vous penser à un travail pour que le compilateur génère le code correct (autre que revenir à l'annulation de retour - ce qui est plausible pour mon scénario, et ma prochaine étape probable, sauf une meilleure suggestion)?
  2. Vous savez comment signaler cela à Microsoft? [Je n'ai pas eu de chance de trouver des êtres humains à l'autre bout pour donner ce genre d'information détaillée afin qu'il ne soit pas nul)
+0

Votre typedef ne devrait-il pas indiquer que le type de retour est différent? –

+0

[email protected] # Désolé, mon code est en flux - ma question a été corrigée pour refléter le code corrigé (le compilateur ne compilera même pas à moins d'être d'accord) – Mordachai

+1

"Pour être certain, le code compile & s'exécute". Si c'est le cas, cela ressemble à un problème de débogueur, pas à un problème de compilateur. Les bogues Visual Studio peuvent être signalés dans [Microsoft Connect] (http://connect.microsoft.com/). –

Répondre

3

Ceci est en effet un bogue dans le compilateur Visual C++. Voir le rapport de bug "miscompilation of aggregate initializer with lambdas inside". Pour contourner ce problème, pensez à ne pas utiliser l'initialisation d'agrégat ici. Au lieu de cela, vous pouvez utiliser un std::vector: (. Si vos données est une application de noms à lambdas, vous pouvez également envisager d'utiliser un std::map, ce qui pourrait fournir de meilleures performances ou peut-être plus facile à utiliser)

UpdateField MakeUpdateField(char const* label, LambdaType const lambda) 
{ 
    UpdateField f = { label, lambda }; 
    return f; 
} 

std::vector<UpdateField> fields = ([]() -> std::vector<UpdateField> 
{ 
    std::vector<UpdateField> data; 
    data.push_back(MakeUpdateField("Identity", [] { /* contents omitted */ })); 
    data.push_back(MakeUpdateField("X1",  [] { /* contents omitted */ })); 
    data.push_back(MakeUpdateField("Y1",  [] { /* contents omitted */ })); 
    return data; 
})(); 

+0

Il s'agit d'une liste très courte et linéaire d'éléments utilisés uniquement dans ce scénario (mettre à jour la liste des champs dans l'interface utilisateur 1, 2, .., n) où n <15, donc facile à faire. :) – Mordachai

+0

Tant que N est connu, à la compilation, vous pouvez substituer 'std :: array ' dans l'exemple que je montre (et remplacer 'push_back' par un accès direct aux éléments (en utilisant' [n] ') et vous aurez effectivement le même code que vous avez maintenant, sauf qu'il va contourner le bug ci-dessus –

+0

L'un des avantages de l'initialisation en ligne est que le compilateur peut déterminer combien sont dans la table - et il peut être ajouté ou réduit à volonté sans changer le reste du code.Je vais simplement utiliser le mécanisme push_back pour le conserver;) – Mordachai

Questions connexes