Кресты / Говнокод #24805 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
#include <iostream>
#include <algorithm>
#include <functional>
#include <map>
#include <string>

int main()
{
    using namespace std::placeholders;

    std::map<std::string, int> karta;
    std::vector<std::string> goroda{ "foo", "bar", "foo" };

    std::for_each(goroda.begin(), goroda.end(), std::bind(
        static_cast<
            std::pair<decltype(karta)::iterator, bool>
            (decltype(karta)::*)(const decltype(karta)::key_type&, decltype(karta)::mapped_type&&)>
                (&decltype(karta)::insert_or_assign),
        std::ref(karta),
        _1,
        std::bind(
            std::plus<decltype(karta)::mapped_type>(),
            1,
            std::bind(
                static_cast<decltype(karta)::mapped_type&(decltype(karta)::*)(const decltype(karta)::key_type &)>
                (&decltype(karta)::operator[]),
                std::ref(karta),
                _1))
    ));
    std::cout << "foo: " << karta["foo"] << "\nbar: " << karta["bar"] << '\n';
    
    return EXIT_SUCCESS;
}

#24802, переписанный в функциональном modern C++ стиле.
Переделать бы ещё это под итераторы, чтобы двух обращений к мапе не было…

Запостил: gost gost, (Updated )

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

  • Твой код выглядит как выхлоп g++ об ошибке в коде с шаблонами.
    Ответить
  • Можно обойтись без "decltype"?
    Ответить
    • Начинаем деобфускацию:
      #include <iostream>
      #include <algorithm>
      #include <functional>
      #include <map>
      #include <string>
      #include <vector>
      using namespace std;
      using namespace std::placeholders;
       
      typedef map<string, int> Map;
       
      int main()
      {
          Map karta;
          vector<string> goroda{ "foo", "bar", "foo" };
       
          for_each(goroda.begin(), goroda.end(), bind(
              static_cast<
                  pair<Map::iterator, bool>
                  (Map::*)(const string&, int&&)>
                      (&Map::insert_or_assign),
              ref(karta),
              _1,
              bind(
                  plus<int>(),
                  1,
                  bind(
                      static_cast<int&(Map::*)(const string &)>
                      (&Map::operator[]),
                      ref(karta),
                      _1))
          ));
          cout << "foo: " << karta["foo"] << "\nbar: " << karta["bar"] << '\n';
       
          return 0;
      }
      Ответить
      • Деобфусцировал полностью, проверь:
        #include <iostream>
        
        using namespace std;
        
        int main() {
            cout << "foo: 2\nbar: 1\n";
            return 0;
        }
        Ответить
        • Это ебанутый сишный синтаксис каста указателя на нестатический метод класса Map.
          Проблема в том, что operator[] и insert_or_assign() — перегружены, поэтому нам надо явно задать возвращаемые и принимаемые типы. В коде это делается через приведение к указателю на функцию нужного вида.
          Ответить
          • О да. Взятие адреса перегруженной функции это вещь достаточно неочевидная.
            Хотя, с другой стороны, логичная - при вызове же функция выбирается автоматически по аргументам, ну и тут происходит похожее.
            Ответить
            • В крестах функция выбирается не при вызове, а ещё во время компиляции.

              Перегрузки в объектном файле имеют разные имена (google: mangling). Допустим, нужно написать функцию sin для разных типов данных:
              float sin(float x);
              double sin(double x);
              long double sin(long double x);

              В объектном файле (и в ассемблере, если компиляция идёт через ассемблер) они будут именоваться как-то так:
              sin!@#pituh$%^f&
              sin!@#pituh$%^d&
              sin!@#pituh$%^ld&

              Нечитаемые символы я написал наобум. Главное здесь, что где-то в добавленны символах зашифрованы имена типов аргументов (я добавил f, d, ld).

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

              Взятие же адреса перегруженной функции тяжело реализовать, потому что компилятор должен подобрать какой-нибудь из вариантов... Как ему указать вариант, если аргументов нет?
              Ответить
              • Как всё сложно. Поэтому я за "PHP".
                Ответить
                • Как всё сложно. Поэтому я за "Си".

                  Правда, там тоже любят чуток мангальнуть (winmain@16). Тут 16 это понятно что, да?
                  Ответить
                    • угумс

                      но это ибет только линкера (или лоадера) коий шукает ее чтобы сделать точкою входа

                      а вот обычные функции конечно экспортятся нормально, так что там можно взять указатель на функцию вообще влегкую. Именно потому я за си
                      Ответить
                  • В "Watcom C" такой мангалинг можно задать прагмой:
                    #pragma aux _ _ stdcall "_ *@nnn" \
                    parm routine [] \
                    value struct struct caller [] \
                    modify [eax ecx edx]

                    Да, и __cdecl, и __syscall, и __pascal, и собственный __watcall там где-то заданы прагмой и их можно переопределять. Шаблон "_ *@nnn" здесь означает добавить впереди знак подчёркивания и в конце собаку и размер аргументов.
                    Ответить
                  • Вообще эту @16 любят не в голой сишке, а в соглашении stdcall, которое используется в Win32. Зачем так сделали, не знаю. Вероятно, предполагали возможность добавления новых функций с тем же именем, но с другим количеством аргументов. Стек при stdcall всё равно чистит сама функция (ret 16 или типа того), а не вызывающий блок.
                    Ответить
                    • ахаха, перегрузка по аргументам

                      это чтобы писать не на сишечке а на япах коие ее умеют?
                      а поцкаль умел?
                      Ответить
              • >>В крестах функция выбирается не при вызове, а ещё во время компиляции.
                невиртуальная тоже?
                Ответить
          • Пример кода можно? Желательно не такой ебанутый, как в посте. И желательно на "PHP".
            Ответить
            • Из Гугла:
              void f(char c);
              void f(int i);
              
              // Uses the void f(char c); overload
              std::for_each(s.begin(), s.end(), static_cast<void (*)(char)>(&f));
              
              // Uses the void f(int i); overload
              std::for_each(s.begin(), s.end(), static_cast<void (*)(int)>(&f));


              В «PHP» нет перегрузки функций, поэтому сделать точный перевод нельзя.
              Ответить
    • Можно, но тогда код станет чуть-чуть понятнее.
      using Karta = std::map<std::string, int>;
      Karta karta;
      std::vector<std::string> goroda{ "foo", "bar", "foo" };
      
      std::for_each(goroda.begin(), goroda.end(), std::bind(
          static_cast<
              std::pair<Karta::iterator, bool>
              (Karta::*)(const Karta::key_type&, Karta::mapped_type&&)>
                  (&Karta::insert_or_assign),
          std::ref(karta),
          _1,
          std::bind(
              std::plus<Karta::mapped_type>(),
              1,
              std::bind(
                  static_cast<Karta::mapped_type&(Karta::*)(const Karta::key_type &)>
                  (&Karta::operator[]),
                  std::ref(karta),
                  _1))
      ));

      Или даже:
      using Karta = std::map<std::string, int>;
      using Key = Karta::key_type;
      using Value = Karta::mapped_type;
      Karta karta;
      std::vector<std::string> goroda{ "foo", "bar", "foo" };
      
      std::for_each(goroda.begin(), goroda.end(), std::bind(
          static_cast<
              std::pair<Karta::iterator, bool>
              (Karta::*)(const Key&, Value&&)>
                  (&Karta::insert_or_assign),
          std::ref(karta),
          _1,
          std::bind(
              std::plus<Value>(),
              1,
              std::bind(
                  static_cast<Value&(Karta::*)(const Key &)>
                  (&Karta::operator[]),
                  std::ref(karta),
                  _1))
      ));
      Ответить
  • Не могу понять. bind (который 21 строка) принимает 3-м аргументом не число, как какую-то хуйню. Как это?
    Ответить
    • Это особенность bind'а: если в его аргумент передать результат вызова другого bind'а («bind expression»), то он сработает как композиция функций (причём вложенный bind позаимствует номера аргументов у основного):
      auto f1 = [](int x, int y, int z) { std::cout << "f1(" << x << ", " << y << ", " << z << ")" << std::endl; };
      auto f2 = [](int x, int y) { std::cout << "f2(" << x << ", " << y << ")" << std::endl; return 200; };
      auto f3 = std::bind(f1,
                          _1,
                          std::bind(f2, _2, 100),
                          _2);
      f3(1, 2);
      // =>     f2(2, 100)
      //        f1(1, 200, 2)
      // Вызывается f2, которой передаются второй переданный аргумент и 100,
      // результат f2 передаётся вторым аргументом в f1


      Подробнее: https://en.cppreference.com/w/cpp/utility/functional/bind
      Ответить

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

Из-за тебя ушел bormand, guest!

    А не использовать ли нам bbcode?


    8