Previous investigation in Can I make a table of String + lambdas that have the same signature? showed me that I can in fact have a table of strings + lambdas in VS2010.
Things were looking good while the lambdas were void return type. But having tried to change them to bool return type, the compiler seems to get it wrong, or there’s some sort of a memory corruption error… something’s not right in C++ land…
The following illustrates the scenario:
// 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("");
}
}
The above worked beautifully when the lambda’s return type is void (i.e. ->bool is omitted, and the various lambda bodies are modified to not return anything, etc.).
However, it’s useful to me to have them return a bool indicating whether or not the lambda was able to process data for that field, and if not, to have the caller handle clearing that field.
To be certain, the code compiles & runs… until it hits this code & CRASHES. Looking at “MenuFields[]” in the debugger shows garbage for most of the MenuField[x].label addresses (sometimes one of them is correct, but I haven’t figured out what the pattern is).
I thought maybe the compiler was glitching over the syntax of the lambda embedded in the static initialization list, but I’m not sure what I can do about that?
I tried this variation:
{ "Identity", LambdaType( [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { return pMenuItem->SetText(FString("%s/%.1f", pNearestItem->thissec->name, pNearestItem->number/10.0)), true; } ) },
The compiler likes this equally well, but it results in the same corrupted table data.
Similarly, putting parenthesis around the entire lambda is okay by the compiler, and equally corrupt at runtime.
So, some questions:
- Do you see something I overlooked?
- Can you think of a work around to get the compiler to generate the correct code (other than going back to void return – which is plausible for my scenario, and my likely next step barring a better suggestion)?
- Know how best to report this to Microsoft? [I have had no luck finding actual human beings on the other end to give this sort of detailed info to so that it actually goes somewhere other than >nul)
This is indeed a bug in the Visual C++ compiler. See the bug report “miscompilation of aggregate initializer with lambdas inside”.
As a workaround, consider not using the aggregate initialization here. Instead, you could use a
std::vector:(If your data is a mapping of names to lambdas, you might also consider using a
std::map, which might provide better performance characteristics or might be easier to use.)