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

    +17

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    auto mesh=mesh_loader.load("wall.x").
    map([](auto m){
      return m.SetPosition(0.f, 0.f, 0.f);
    }).map([=](auto m){
      return m.SetRotation(xr,yr,zr);
    }).map(::std::bind(&Mesh::SetSize, _1, 90, 60, 90)).
    map(&Mesh::RecalculateNormals);

    Запостил: LispGovno, 10 Мая 2014

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

    • оттуда
      Ответить
      • интересно было бы глянуть сколько объектного кода вот это производит. но лень.
        Ответить
        • Скорее всего немного. Если функция map() короткая - лямбда и итераторы mesh'а в нее заинлайнятся, и получится обычный цикл в котором делают m.SetPosition(). Ну мне так кажется, проверять тоже лень.
          Ответить
      • Ойойой. Да я ради прикола это написал.
        Ответить
    • А как это красивее переписать на крестах?
      Ответить
      • Можно посетителя написать, в меше реализовать четыре перегрузки для accept(), каждая из которых будет выполнять SetPosition, SetRotation и т.д. Будет все как в книжке.
        Ответить
      • Ну хотя бы как-то так, раз уж оно fluent interface имеет...
        auto mesh = mesh_loader.load("wall.x").map([=] (auto m) {
            return m.SetPosition(0.f, 0.f, 0.f)
                .SetRotation(xr,yr,zr)
                .SetSize(90, 60, 90)
                .RecalculateNormals();
        });
        Ответить
        • На самом деле там один меш и как раз изначально был fluent interface, вообще без лямбды. А lamer007 предложил сделать лямбду чтобы не проверять после каждой операции mesh на null (исключения бросать тоже по условию нельзя). Во всяком случае я так происходившее понял.
          Ответить
          • А откуда там может быть null?
            Там во-первых ссылка, в которую пихать 0 не особо прилично (хотя и возможно).
            Во-вторых во fluent интерфейсе обычно тупо *this возвращают.

            Да и какие в жопу исключения и ошибки в повороте мешей и в пересчете нормалей?! Вы там на ГД совсем уже упоролись? Тараса на вас нет :)
            Ответить
            • ну да, вместо null lamer007 говорил что-то о boost::optional. А так - дискуссия шла о том что fluent интерфейс неудобен если посреди операции может возникнуть ошибка. Меш как пример.
              Ответить
              • Ну можно сделать как в иостримах - флаг/код ошибки. И пусть все операции проверяют его и ничего не делают, если он ненулевой. А после вызова пачки операций его можно проверять и сбрасывать.
                Ответить
                • > Ну можно сделать как в иостримах - флаг/код ошибки. И пусть все операции проверяют его
                  Картинку со злым котиком и злым буратином сам нагуглишь.
                  Ответить
                  • Не ну раз чуваки отключили исключения. И каждый раз проверять результат тоже не хотят. И пародия на монаду Maybe через аналог boost::optional или че-то.map().map().map() им не нравится. То что же остается?

                    Правильно - жрать кактус со статусом. Тем более прецедент в стандартной либе уже есть.
                    Ответить
                    • optional можно и вручную проверять без всяких флагов типа
                      if(!mesh)
                        mesh = mesh.rotate(x,y,z);
                      Ответить
                    • > Не ну раз чуваки отключили исключения.
                      В гугле они не без основания запрещены.
                      Ответить
            • >Там во-первых ссылка, в которую пихать 0 не особо прилично (хотя и возможно).
              По-моему здесь поднимали этот вопрос, и выяснили, что никаким путем, кроме как через UB, 0 в ссылку попасть не может.
              Ответить
              • Я так понял тут не ссылка, а ::boost::option
                Ответить
                • > тут не ссылка, а ::boost::option
                  А у нее разве можно методы вызывать через точку?
                  Ответить
                • > ::boost::option
                  ::boost::optional
                  Ответить
          • где тут может быть nullptr?
            m.SetPosition(0.f, 0.f, 0.f) возвращает указатель на себя?
            Ответить
    • auto normalized_mesh = mesh_loader.load("wall.x")
        << mesh::set_pos(.0, .0, .0)
        << mesh::set_rot(xr, yr, zr)
        << mesh::set_size(90, 60, 90)
        << mesh::recalculate_normals;
      Ответить
      • Тоже в той теме предлагали. lamer007 сказал что порядок выполнения функций будет не определен.
        ---
        Вот сама тема если что http://www.gamedev.ru/flame/forum/?id=188947
        Ответить
        • > lamer007 сказал что порядок выполнения функций будет не определен
          Вот не надо мне это приписывать. Порядок обратный же. А вот порядок вычисления аргументов во всем этом большом выражении без sequence point будет чуть более чем UB.
          В частности допустим
          >>= mesh::set_rot(xr, yr, zr)
          >>= mesh::set_size(xr, yr, zr)
          И допустим в set_rot и в set_size делается для первого аргумента ++xr. В этот момент UB во все поля.
          Ответить
          • А, дошло, т.е. UB будет только если мы в нескольких функциях изменяем одинаковые данные? Или достаточно в одной функции записать их, а в другой прочитать.

            В данном случае по-моему очевидно что recalculate_normals будет использовать координаты модифицированные set_rot и set_size.
            Ответить
            • > достаточно в одной функции записать их, а в другой прочитать.
              да

              > В данном случае по-моему очевидно что recalculate_normals будет использовать координаты модифицированные set_rot и set_size.
              Хм. То есть когда мы модифицируем меш, то в каком порядке вызовется операции set_rot и set_size мы не знаем и эти операции должны быть ассоциативные? Тут надо попросить Романа написать свою ::roman::optional с оператором >>=. А то как то без реализации не удаётся ответить на этот вопрос. Да и вообще пойду ка я спать.
              Ответить
              • > optional с оператором >>=
                Посмотри на ассоциативность оператора >>= и ты поймёшь, почему такой подход не сработает.
                Ответить
        • > порядок выполнения функций будет не определен
          mesh::set_pos, mesh::set_rot - да, в хер знает каком порядке выполнятся (но т.к. они еще не трогали меш, и у них нет побочных эффектов - всем пофиг). А дальше все операторы << отработают в правильном порядке слева направо (и вызовут функции, которые им вернули mesh::set_pos и т.п.?).
          Ответить
          • > mesh::set_pos, mesh::set_rot - да, в хер знает каком порядке выполнятся
            Да, выше я ерунду сказал. Даже этот порядок не известен.
            Ответить
      • Вы предлагаете для меша перегрузить оператор потока?
        Ну тут из минусов методы mesh::* не должны иметь постэффектов вне меша в том числе и через параметры, тк перестановка местами порядка вызовов и на счет постэффектов в параметрах - UB.

        > mesh::set_pos
        А это типа статический метод класса mesh, возвращающий лямбду от аргумента типа mesh?
        Ответить
        • закрашивать код зелёным я уж не стал, но всё же

          > из минусов методы mesh::* не должны иметь постэффектов вне меша
          Честно говоря, мне сложно представить ситуацию, где они действительно нужны. Если ты пихаешь инкремент и потоковый вывод в одно сложное выражение, то лучше тебе сразу убиться об стену, уменьшив энтропию вселенной.

          > А это типа статический метод класса mesh
          или вложенная структура с 3-мя полями, для которой перегружен оператор вывода

          я бы в данном случае не стал выпендриваться флюент интерфейсами, они как-то не очень сочетаются с семантикой объектов. KISS:
          mesh m = loader.load("wall.x");
          m.set_pos(.0,.0,.0);
          m.set_rot(xr, yr, zr);
          m.set_size(90, 60, 90);
          m.recalculate_normals();
          IMHO, идеально
          Ответить
          • > Если ты пихаешь инкремент и потоковый вывод в одно сложное выражение, то лучше тебе сразу убиться об стену, уменьшив энтропию вселенной.
            Так тут не потоковый вывод, а другая конструкция. Ну и как я говорил xr=xr+1 может быть внутри метода set_rot и set_size например
            xr соответственно принимается по ссылке. А это между прочем вполне логичный способ для взаимодействия между методами, вызванные в этой псевдоманаде.
            Ответить
          • > mesh m = loader.load("wall.x");
            > m.set_pos(.0,.0,.0);
            ну а допустим исключения все же запрещены. Не все же работают вне гугла
            Ответить
            • > ну а допустим исключения все же запрещены
              ну ты же понимаешь, что в таком случае кто-то должен написать if. Либо пользователь api, либо автор.
              Если стоит задача избавить клиента библиотеки от постоянных проверок, можно соорудить что-то вроде этого
              error e; // current status object
              mesh m = loader.load("wall.x", e);
              if (e) { std::cout << "mesh is broken\n"; return e; }
              m.set_pos(.0, .0, .0, e);
              m.set_rot(rx, ry, rz, e);
              m.set_size(90, 60, 90, e);
              m.recalculate_normals(e);
              // check e
              Ответить
              • Но это ведь как раз та фигня со статусом, о которой я написал выше, и от которой воротил нос ЛиспГовно ;)
                Ответить
                • > как раз та фигня
                  я разве спорю... меня спросили, я ответил.

                  > от которой воротил нос ЛиспГовно
                  Ну волшебства ни бывает. Разделяйте "грязный" код и "чистый", пусть ошибка будет невозможной :)
                  Ответить
                  • > пусть ошибка будет невозможной
                    Вот на этом примере она точно невозможна. Там чистейшая математика, в которой ломаться вообще нечему (кроме load'а).
                    Ответить
                    • А вдруг при пересчете нормалей деление на ноль.
                      Ответить
                      • Это как? :)

                        В модели попался треугольник нулевой площади (если мы строим нормали по координатам вершин) или нормали изначально были нулевой длины (если мы считаем на основе нормалей из модели)?
                        Ответить
                        • > В модели попался треугольник нулевой площади
                          Легко. Часто нужны вырожденные треугольники чтобы используя Triangle Strips
                          http://msdn.microsoft.com/en-us/library/windows/desktop/bb206274(v=vs.85).aspx
                          начать рисовать новую полоску треугольников, не связанную со старыми
                          Ответить
                          • Ну дык в таком случае это не ошибка, и прерывать вычисления не нужно. Просто пропустить такой треугольник да и все.
                            Ответить
          • Плохо при имлицит параллелизме. На первый взгляд не понятно, какие из методов можно выполнить параллельно, а какие - нет. Мап лучше, т.как можно явно указать, что можно выполнять параллельно, а что нет.

            ПС. set_size после set_rotation - плохой порядок, не интуитивный. Как правило нам интересны размеры до поворота, а не после.
            Ответить
            • > Мап лучше, т.как можно явно указать, что можно выполнять параллельно, а что нет.
              А можно пример и почему код выше плох при имлицит параллелизме?
              Ответить
              • Позиционирование и размеры друг другу не мешают, их можно задать в любом порядке (т.е. параллельно в том числе). А поворачивать нужно либо до, либо после, от этого зависит конечный результат. Если записать это как:
                позиционировать
                повернуть
                растянуть

                то мы выбираем не самую удачную стратегию: мы могли бы выполнить этот алгоритм в два шага:
                { позиционировать, растянуть }
                повернуть

                или

                повернуть
                { позиционировать, растянуть }

                А выбрали делать в три шага.
                Ответить
            • > Плохо при имлицит параллелизме
              >> C++

              В крестах имплисит параллелизм бывает разве что в виде SIMD в циклах у умных компиляторов. Если хочется выполнять что-то КакПараллельно, нужно в любом случае существенно переписывать код.
              Выполнять эти методы параллельно я особого смысла не вижу, скорее имеет смысл производить какие-то трансформации на разных участках матрицы параллельно, для этого нужно совсем всё переписать.
              Если речь идёт о потенциально опасном переупорядочивании вызовов методов на некоторых архитектурах, тот тут между вызовами есть зависимость по данным - деструктивные вызовы методов mesh, так что компилятор не должен позволять менять порядок их выполнения.

              > ПС. set_size после set_rotation - плохой порядок

              вопросы к топик стартеру
              Ответить
    • auto mesh = mesh_loader.load("wall.x").value_or(defaultMesh)
        .position(0.f, 0.f, 0.f).value_or(defaultMesh)
        .rotation(xr,yr,zr).value_or(defaultMesh)
        .size(90, 60, 90).value_or(defaultMesh)
        .RecalculateNormals().value_or(defaultMesh);
      Ответить
      • Убрать точки и скобки, и Форт получится.
        Ответить
        • Тогда еще порядок поменять надо:
          " wall.x" load_mesh defaultMesh @ valueOr
          0.0f 0.0f 0.0f position defaultMesh @ valueOr
          xr @ yr @ zr @ rotation defaultMesh @ valueOr
          90 60 90 size defaultMesh @ valueOr
          recalculateNormals defaultMesh @ valueOr
          mesh !
          Ответить
    • Толсто.
      Ответить
    • ───────────────────────────────
      ──────────▄▄▄▄▄▄▄▄▄▄▄──────────
      ─────▄▄▀▀▀▀──────────▀▀▄▄──────
      ───▄▀───────────────────▀▀▄────
      ──█────────────────────────█───
      ─█─────────────────────▄▀▀▀▀▀█▄
      █▀────────────────────█────▄███
      █─────────────────────█────▀███
      █─────▄▀▀██▀▄─────────█───────█
      █────█──████─█─────────▀▄▄▄▄▄█─
      █────█──▀██▀─█───────────────█─
      █────█───────█──────────────▄▀─
      █────▀▄─────▄▀──▄▄▄▄▄▄▄▄▄───█──
      █──────▀▀▀▀▀────█─█─█─█─█──▄▀──
      ─█──────────────▀▄█▄█▄█▀──▄▀───
      ──█──────────────────────▄▀────
      ───▀▀▀▄──────────▄▄▄▄▄▄▀▀──────
      ────▄▀─────────▀▀──▄▀──────────
      ──▄▀───────────────█───────────
      ─▄▀────────────────█──▄▀▀▀█▀▀▄─
      ─█────█──█▀▀▀▄─────█▀▀────█──█─
      ▄█────▀▀▀────█─────█────▀▀───█─
      █▀▄──────────█─────█▄────────█─
      █──▀▀▀▀▀█▄▄▄▄▀─────▀█▀▀▀▄▄▄▄▀──
      █───────────────────▀▄─────────
      Ответить
    • минута рекламы на ГК - желающие посмотреть на живого мейерса - велкам
      http://tech.yandex.ru/events/yagosti/msk-jun-2014/
      Ответить

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