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

    0

    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
    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, 24 Сентября 2018

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

    • https://wandbox.org/permlink/Kr2SqCQCNfsthfZw
      Ответить
    • пиздец
      Ответить
    • Твой код выглядит как выхлоп g++ об ошибке в коде с шаблонами.
      Ответить
      • И правда: https://wandbox.org/permlink/lGpinOKJcnlgpcf4
        Ответить
    • Можно обойтись без "decltype"?
      Ответить
      • Можно, но тогда код станет чуть-чуть понятнее.
        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))
        ));
        Ответить
      • Начинаем деобфускацию:
        #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::*?
          Ответить
          • Это ебанутый сишный синтаксис каста указателя на нестатический метод класса Map.
            Проблема в том, что operator[] и insert_or_assign() — перегружены, поэтому нам надо явно задать возвращаемые и принимаемые типы. В коде это делается через приведение к указателю на функцию нужного вида.
            Ответить
            • О да. Взятие адреса перегруженной функции это вещь достаточно неочевидная.
              Хотя, с другой стороны, логичная - при вызове же функция выбирается автоматически по аргументам, ну и тут происходит похожее.
              Ответить
              • В крестах функция выбирается не при вызове, а ещё во время компиляции.

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

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

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

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

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

                    Правда, там тоже любят чуток мангальнуть ([email protected]). Тут 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» нет перегрузки функций, поэтому сделать точный перевод нельзя.
                Ответить
    • Не компилится
      https://ideone.com/fi12E8
      Ответить
      • Слишком старая версия C++. Не модерновая.
        Ответить
      • На wandbox.org можно выбирать версию компилятора и стандарт, проверь
        Ответить
    • Не могу понять. 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
        Ответить
    • Я жду.
      Ответить
      • В недавнем говнокоде какой-то восьмой гуест, наверняка знаток функционального программирования, представил композицию функций на «PHP». Вот пусть он и переводит.
        Ответить

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