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

    +2

    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
    struct A { 
      virtual int transmogrify();
    };
     
    struct B : A {
      int transmogrify() override { new(this) A; return 2; }
    };
     
    int A::transmogrify() { new(this) B; return 1; }
     
    static_assert(sizeof(B) == sizeof(A));
    
    int main() {
      A i;
      int n = i.transmogrify();
      // int m = i.transmogrify(); // undefined behavior
      int m = std::launder(&i)->transmogrify(); // OK
      assert(m + n == 3);
    }

    Yo dawg, we heard you like kostyli, so we put our kostyli into your kostyli, so that you can use kostyli to support our kostyli!

    https://en.cppreference.com/w/cpp/utility/launder

    А если серьезно, мне еще не удалось соорудить пример, чтоб код с std::launder и без него работали по разному.

    Запостил: Elvenfighter, 24 Сентября 2018

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

    • Кстати, теперь мою старую кучку можно делать правильно.
      http://govnokod.ru/11417
      Ответить
    • > std::launder

      Чисти указатель, животное
      Ответить
    • показать все, что скрытоvanished
      Ответить
      • Ну, std::launder не разыменовывает указатель. Он тупо возвращает то, что получил.

        Внутри std::launder находится компиляторо-специфический костыль, который можно назвать "optimization barrier", который сигнализирует компилятору "смотри, жывотное, что под указателем, а не в свои сраные кеши". Где-то читал, что в GCC std::launder был реализован просто:
        template <typename _Tp>
        _Tp * launder(_Tp *__p) { return __p; }


        Но потом хлопцы из LLVM/Clang подсуетились и показали who is who, продемонстрировав пример, когда гнутая реализация делала ересь.
        Ответить
        • показать все, что скрытоvanished
          Ответить
          • В первом случае коспилятор мог бы закостылизировать закешировать (на стеке, напр.) значение p->n (ведь X::n -- const int и он не должен менятся, так?). И выражение мог бы p->n заменить на использование кеша. Т.е. вместо ожидаемого 5 в const int b внезапно при выполнении окажется 3.

            Во втором случае комитет вероятно пошел еще дальше, и сказал "А что если компилятор закеширует целую структуру под указателем? Ведь мы итак знаем, что итак X::n у нас const и читать его после замены без std::launder -- UB." И дальше все так же как и в предыдущем пункте.
            Ответить
    • показать все, что скрытоvanished
      Ответить
      • В PHP сабжевой фичи нет. А как подменять "this" в PHP я не ведаю.
        Ответить
      • Поддерживаю.
        Ответить
        • Если отпишешь тут из-под всех своих фаек, то я подумаю :3
          Ответить
        • Ввиду некоторой ограниченности языка «PHP» (в частности, невозможности перезаписать $this), точный перевод невозможен. Однако, позволив себе некоторую вольность, этот код можно перевести вот так:
          <?
          function sizeof_($class)
          {
              $before = memory_get_usage();
              $ret = new $class();
              $after = memory_get_usage();
              return ($after - $before);
          }
          
          function launder($p) {
              return $p;
          }
          
          class A {
              function transmogrify_cpp_translation() {
                  return 1;
              }
              function transmogrify() {
                  return unserialize(preg_replace('/^O:\d+:"[^"]++"/','O:1:"B"', serialize($this)));
              }
          }
          
          class B extends A {
              function transmogrify_cpp_translation() {
                  return 2;
              }
              function transmogrify() {
                  return unserialize(preg_replace('/^O:\d+:"[^"]++"/','O:1:"A"', serialize($this)));
              }
          }
          
          if (sizeof_("A") != sizeof_("B")) {
              echo "error: static assertion failed";
          }
          
          $i = new A();
          $i = $i->transmogrify();
          $n = $i->transmogrify_cpp_translation();
          // $i = $i->transmogrify(); // undefined behavior
          $i = launder($i)->transmogrify();
          $m = $i->transmogrify_cpp_translation();
          if ($n + $m != 3) {
              echo "assertation \"\$n + \$m != 3\" failed.";
          }
          ?>

          http://sandbox.onlinephpfunctions.com/code/b7e8dd12f4d3914031e4784fd6768b1f452f3c41
          Ответить

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