Обходной путь для ошибки VS2010 в таблице лямбд? - PullRequest
1 голос
/ 09 ноября 2011

Предыдущее исследование в Могу ли я составить таблицу из String + лямбда-символов с одинаковой подписью? показал, что на самом деле я могу иметь таблицу строк-лямбда-выражений в VS2010.

Вещи выглядели хорошо, в то время как лямбды были пустого типа возврата.Но попытавшись изменить их на тип возвращаемого значения bool, компилятор, похоже, ошибся, или есть какая-то ошибка повреждения памяти ... что-то не так в земле C ++ ...

Следующее иллюстрируетСценарий:

// 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("");
    }
}

Вышеописанное прекрасно работало, когда лямбда-тип возврата пуст (т.е. -> bool опущен, а различные лямбда-тела изменены, чтобы ничего не возвращать и т. д.).

Тем не менее, мне полезно, чтобы они возвращали логическое значение, указывающее, была ли лямбда в состоянии обработать данные для этого поля, и если нет, чтобы обработчик вызовов очищал это поле.

конечно, код компилируется и запускается ... пока не попадет в этот код и не ударится.Глядя на «MenuFields []» в отладчике, можно увидеть мусор для большинства адресов MenuField [x] .label (иногда один из них правильный, но я не выяснил, что это за шаблон).

Я подумал, что, возможно, компилятор не справился с синтаксисом лямбды, встроенной в список статической инициализации, но я не уверен, что я могу с этим поделать?

Я попробовал этот вариант:

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

Компилятору это нравится одинаково хорошо, но это приводит к тем же поврежденным данным таблицы.

Аналогично, установка круглых скобок вокруг всей лямбды является приемлемой для компилятора и одинаково повреждена во время выполнения.

Итак, несколько вопросов:

  1. Видите ли вы что-то, что я упустил?
  2. Можете ли вы придумать, как компилятору сгенерировать правильный код (кроме возврата назад)аннулировать возврат - что является вероятным для моего сценария, и мой вероятный следующий шаг, за исключением лучшего предложения)?
  3. Знаете, как лучше всего сообщить об этом в Microsoft?[Мне не повезло найти реальных людей на другом конце, чтобы дать такую ​​подробную информацию, чтобы она действительно шла куда-то, кроме> nul)

1 Ответ

3 голосов
/ 09 ноября 2011

Это действительно ошибка в компиляторе Visual C ++.См. Отчет об ошибке "неправильная компиляция агрегатного инициализатора с лямбдами внутри" .

В качестве обходного пути, не используйте здесь агрегатную инициализациюВместо этого вы можете использовать std::vector:

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

(Если ваши данные представляют собой сопоставление имен с лямбдами, вы можете также рассмотреть возможность использования std::map, который может обеспечить лучшие характеристики производительности или можетпроще в использовании.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...