1. C++ / Говнокод #5752

    +167

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    const StringId sidPathName = [=]() -> const char* const
    {
    	switch (path)
    	{
    		case PATH_TOWN_HERO:
    			return "game/careers/town_hero";
    		case PATH_MAD_SCIENTIST:
    			return "game/careers/mad_scientist";
    		case PATH_SPECIAL_AGENT:
    			return "game/careers/special_agent";
    		case PATH_PSYCHIC_PAINTER:
    			return "game/careers/psychic_painter";
    		default:
    			RZT_ASSERT(false);
    			return "";
    	}
    }();

    Не удержался: давно мечтал о возможности ставить const на такие переменные, не отказываясь при этом от свитчей!

    Запостил: Kirinyale, 21 Февраля 2011

    Комментарии (64) RSS

    • Началось.
      Ответить
    • >= [=]() ->
      ШТОЭТА? Плюсы скатываются в перл?
      Ответить
      • ЭТА - лямбда+замыкание из нового стандарта.
        Раз уж получил в наследство от других говнокодеров проект под студию 2010 - грех не отыграться.
        Ответить
        • Новый стандарт - это который C++0x?
          Ответить
          • Он самый.
            Ответить
            • Ужас какой.
              Ответить
              • и вовсе и не ужас
                лямбды -- отличный способ писать нечитаемый говнокод, но например по linq, var и extension methods из C# ему далеко.

                так что С++ еще некоторое время останется заповедником адекватов в море говнокодерства
                Ответить
        • Новый стандарт - новые извращения?))
          Ответить
          • А как же иначе? Без извращений код уныл и пресен!
            Ответить
      • [=]() ->
        В рядах смайликов пополняется.)
        Ответить
        • [=]()->{}();
          Ответить
          • Упс... не скомпилится... Ну да ничего, прорвёмся. :)
            Ответить
        • ты не шаришь. смайл обозначает охуевшего кодера в пулезащитных очках, тщетно пытающегося прострелить себе ногу из airsoft оружия.

          бля. чо они делают со стандартами, а?
          Ответить
          • [=]() ->
            А внизу (справа) это чьи-то яйца около рта? *ROFL*

            А теперь внимание, вопрос знатокам: Чьи?
            Ответить
            • Бьёрна Страуструпа?
              Ответить
              • Блин. Фейл. Знаток спалился.
                Ответить
                • Блин. Полный фейл. Раз знаю ответ, то сам спалился...
                  Ответить
                • Я не утверждал, я выдвинул гипотезу. :)
                  Ответить
              • Страус-труп яиц не может иметь
                Ответить
                • И деньги уходят телезрителю Lure Of Chaos из США, Штат Вирджиния.
                  Счёт 7:0 в пользу телезрителей.
                  Ответить
          • Улучшают, конечно [=]() ->
            Ответить
          • >смайл обозначает охуевшего кодера в пулезащитных очках, тщетно пытающегося прострелить себе ногу из airsoft оружия.

            Скорее смайл обозначает охуевшего кодера с квадратными глазами, после того как он успешно прострелил себе ногу из С++рокетленчера.
            Ответить
            • скорее отстрелил нахуй. вместе с жопой и всем что ниже шеи. ага.
              Ответить
    • Вопрос к знатокам нового стандарта С++0х:
      const StringId sidPathName = [=]() -> const char* const
      - это объявление лямбда-функции sidPathName без параметров с замыканием на локальные переменные по значению. Эта функция возвращает тип const char* const.
      Поправте меня, если я сказал что-то не верно. Не довелось пока поработать с компилятором нового стандарта.
      И так вопрос:
      1)Что в данном случае const StringId?
      2)Где говнокод? В 17ой строчке?
      Ответить
      • 1)
        typedef std::string StringId;

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

        2) Я ещё сам до сих пор пытаюсь понять, насколько это говнокод. В данном конкретном случае можно было бы привычно обойтись выборкой нужной строки из локального статического массива констант, но несколько напрягает необходимость каждый раз отнимать единицу от значения из enum, чтобы получить индекс (0 там называется PATH_NOT_CHOSEN и валидным значением в данном случае не является, всё остальное идёт по порядку).
        Ответить
        • >1)typedef std::string StringId;
          Вопрос не про данный конкретный проект, а про лямбды. Что в данном случае означает тип 'const StringId' с точки зрения семантики лямбд в С++?
          Ответить
          • const StringId sidPathName - объявление константной переменной, получающей возвращаемое лямбдой значение, ставшее результатом вызова, который можно видеть в той самой 17ой строчке.

            Если точнее, в данном случае происходит неявная передача этого значения в конструктор std::string, но не суть.

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

            Ваш кэп.
            Ответить
            • Спасибо. Точно. 17ую строку не сразу заметил... Точнее заметил, но не придал этому значения.
              Ответить
        • Так это не вы говноавтор? Уф-ф, спасибо. А то я уж подумал, что таким гордятся.

          По 0-му индексу можно засунуть фейковое значение ("" или NULL), если лень единицу отнимать. Но делать так не следует. Ведь никто не гарантирует, что значения энумов не изменятся. Лучше превратить лямбду в обычную именованную функцию. А ещё лучше -- вынести такие ресурсы во внешний файл данных.
          Ответить
          • Автор той самой системы локализаций - таки я. Внешний файл здесь не всегда катит (хотя, делай я этот проект с самого начала, с нуля и с вменяемыми сроками, наверняка нашёл бы такую возможность), т.к. те самые "строчки-пути" на самом деле заменяют числовые ид (энумы) реальных строчек. Как показала практика (два законченных проекта), строковые идентификаторы имеют кучу преимуществ перед энумами, и лишь пару недостатков, один из которых в оригинале отпимизирован (строки практически никогда реально не копируются и не сравниваются, т.к. StringId был совсем другим типом), а второй (невалидируемость компилятором) за более чем год активного пользования не создал сколь-нибудь заметных проблем.

            Функцию сделал бы именованной, если бы она использовалась более чем в одном месте, в данном случае - это просто единичный кусок кода загрузки и разбора XML с некоторыми данными.
            Ответить
            • Подумайте о том, кто будет разбирать этот код после вас.
              Ответить
      • >объявление лямбда-функции sidPathName
        Теперь ясно, что не прав. Лямбда-функция безымянная.
        Ответить
        • Лямбда по определению безымянная.
          А присвоить её адрес переменной, сохранив оператор (), можно только двумя (известными мне) способами: через новое значение ключевого слова auto (т.к. нормального типа у неё нет, а тот, что есть - известен только компилятору), либо обернув в стыренную из буста std::function.
          Ответить
          • >А присвоить её адрес переменной
            Ну это я и имел ввиду под именованной лямбдой. Некорректно выразился...
            Ответить
          • >т.к. нормального типа у лямбды нет, а тот, что есть - известен только компилятору
            Разве лямбда не имеет тип функции или указателя на функцию? Если не имеет, то почему? Чем руководствовались создатели стандарта?
            Ответить
            • Скорее всего, тем, что реализовать лямбду, как обычную функцию, можно только при отсутствии замыкания. Если замыкание есть - то это уже не просто указатель на функцию, а объект с некоторым состоянием.
              Ответить
    • >мечтал о возможности ставить const на такие переменные, не отказываясь при этом от свитчей!
      Поясните, пожалуйста, это высказывание.
      Ответить
      • sidPathName, по сути, представляет из себя константу, зависящую от одного значения. Менять её в дальнейшем не нужно - только читать. Но если записать аналогичный код (со switch) без лямбды, её уже придётся делать неконстантной, т.к. в каждом case будет присваивание вместо return. Мелочь, конечно. Но если задаться целью обязательно сохранить const-корректность (о чём и речь), то выходы в старом стандарте - статические массивы (что не подходит для более сложных случаев), либо адские нечитабельные конструкции на тернарном операторе (типа моего предыдущего говнокода, но растущие пропорционально количеству вариантов).
        Ответить
        • Тоесть этот код написали, что-бы любой ценой сохранить константность?
          "Заставь дурака Богу молиться, он и лоб разобьёт."
          Ответить
          • Да. Это плод внезапного озарения, написанный ради двух целей:

            1) сохранение константности (навеяно рефакторингом говнокода, в котором повсеместно применялся const_cast для передачи константного указателя в функцию, ожидающую неконстантного, при том что этой функции его неконстантность реально была нафиг не нужна);

            2) чтобы самому увидеть, как же это извращение будет смотреться (результатом удовлетворён).
            Ответить
            • Это Вы написали? Тогда извиняюсь, что упомянул поговорку. :( Ничего личного.
              Ответить
        • я нихуя не понял.
          где теряется конст, если делать вот так?
          enum enum_path { PATH_NOT_CHOSEN, PATH_TOWN_HERO, PATH_MAD_SCIENTIST, PATH_SPECIAL_AGENT };
          inline const std::string lol(const enum_path path)
          {
          	static const std::string names[] = { "FUCKING ERROR", "town_hero", "mad_scientist", "special_agent" };
          	static const std::string prefix("/game/careers/");
          
          	return prefix + names[path];
          }
          
          далее
          const std::string &string_id = lol(path());
          Ответить
          • Здесь конст не теряется, зато кусок кода попадает достаточно далеко от единственного места своего применения.
            А заинлайнить лямбду компилятор и сам додумается, если не тупой.

            Во всяком случае, комбинация std::for_each с лямбдой на простом цикле по вектору 10-й студией в релизе разворачивается гораздо лучше, чем тот же цикл на итераторах. А то же самое через оператор [] вообще выдаёт войну и мир в дизасме.

            Видимо, придётся смириться с тем фактом, что M$ более не поощряет написание кода, понятного даже школьнику.
            Ответить
    • default:
      			RZT_ASSERT(false);
      			return "";
      Поведение программы в релизе в случае ошибки хорошее...
      Ну и чем
      #include <assert.h>
      не подошёл?
      Ответить
      • Макрос ассерта тоже достался по наследству. А в релизе в случае ошибки любой невалидный ид развернётся в самого себя (в данном случае в пустую строчку).
        Во всяком случае, это лучше, чем то, что будет в варианте со статическим массивом констант, если кто-то втихаря поменяет местами идентификаторы в enum.
        Ответить
        • >ид развернётся в самого себя (в данном случае в пустую строчку).
          Тут всеж лучше падение программы с построением багрепорта, а не тихое замалчивание ошибки.
          Ответить
          • Не думаю, что конечному пользователю (игроку) понравится падение релизной версии программы по причине того, что кто-то потерял одну строчку в локализационной базе или опечатался в ид. Уж лучше немного хрени на экране с возможностью играть дальше.

            А логи, баг-репорты и собственно ассерты есть в дебаге.

            Да, кстати, в прошлых наших проектах было три конфигурации: debug, release и final. В релизе, благодаря включённым оптимизациям, было меньше тормозов, но логи и читы не отключались, а ассерты писали себя в лог, не прерывая выполнения. Финал же (с отключённым всем) собирался только для бета-тестов, окончательных релизов и т.п.
            Ответить
            • >Не думаю, что конечному пользователю (игроку) понравится падение релизной версии программы

              Конечно ему больше понравятся глюки и не верные результаты работы программы, чем возможность получить исправленную версию продукта, в случае наличия ошибки. :D
              Ответить
              • Целевой аудитории, состоящей из 50-летних дам, нажимающих кнопку "отмена" при виде надписи "подождите, распаковываются ресурсы" и не понимающих, какое старая видеокарта и малое количество памяти может иметь отношение к графическим глюкам - да. Больше. А вот претензий от издателя за внезапные креши будет куда меньше.
                Ответить
    • = [=]() ->

      ТарасБ фшоке.
      Ответить
      • Крепитесь. Скоро лямбды и замыкания придут и в паскаль!..
        Ответить
        • := :[=]() ^->
          Ответить
        • В дельфи, кстати, уже есть...
          Ответить
        • Лол
          уже джва года
          http://8vmr.livejournal.com/6114.html
          и при этом выглядит не как говно.
          Эти новые фишечки похоронят С++ под горой нечитаемого символьного мусора.
          Ответить
          • Ничего себе лямбда:
            SomeArray.Sort(    
               function(a,b: Integer): Integer   // Инплейс функция-компаратор
               begin
                  Result := a - b;
               end
            );
            Такое чувство, что обычную функцию не на своё место запихнули...
            Ответить
          • >и при этом выглядит не как говно.
            >Эти новые фишечки похоронят С++ под горой нечитаемого символьного мусора.

            В кои-то веки полностью соглашусь с мнением сего господина.
            Ответить
      • Видимо, ТарасБ просто никогда не пытался расшифровать что-нибудь вроде:

        namespace boost {

        namespace detail { struct none_helper{}; }

        typedef int detail::none_helper::*none_t ;

        } // namespace boost
        Ответить
        • ояебу. зачем такая матрешко?
          Ответить
          • Это Единичный тип (Unit Type) http://en.wikipedia.org/wiki/Unit_type. Тип с единственным значением, отсюда название. По-сути, обычный void, но пустая структура лучше моделирует такой тип.
            Ответить
        • Тут-то просто всё.
          Ответить

    Добавить комментарий